Estatística Computacional
1. O que é Estatística Computacional?
Em geral, a análise estatística envolve conhecimentos sobre um fenômeno para desenvolver um modelo que possibilite o estudo e o entendimento deste fenômeno ou processo que geram os dados. Após a definição do modelo, os dados observados são utilizados para refinar o modelo escolhido ou para selecionar um modelo diferente, ou, ainda, para determinar valores para termos desconhecidos do modelo e para fazer inferência acerca do processo de geração dos dados. Nos dias atuais, este paradigma foi diretamente afetado pelos avanços computacionais, que não somente viabilizaram o desenvolvimento e a aplicação de métodos muito mais sofisticados e complexos, do que aqueles usados antes desta revolução, mas também permitiram o exame de muitas outras visões dos dados.
A partir desses avanços, uma nova visão da Estatística começou a ser estudada, chamada de Estatística Computacional. A Estatística Computacional diz respeito ao desenvolvimento e uso de algoritmos de computador para fornecer soluções numéricas para problemas em estatística que são analiticamente difíceis ou intratáveis. NEsse sentido, alguns problemas para os quais a estatística computacional é aplicada incluem análise exploratória de dados, inferência estatística, otimização, métodos de reamostragem, integração numérica, e simulação de variáveis ou processos aleatórios. Neste curso, iremos abordar todos esses conceitos por meio do Ambiente e Linguagem de Programação R.
2. Ambiente R
Fazendo parte da filosofia do Projeto GNU e estando disponível como Software Livre sob os termos da “Licença Pública Geral do GNU” da Fundação do Software Livre (Free Software Foundation’s GNU General Public License) na forma de código fonte, o R é ao mesmo tempo uma linguagem de programação e um ambiente para computação estatística e gráfica, isto é, trata-se de uma linguagem de programação especializada em computação com dados. Além de gratuito, o R está disponível para uma gama bastante variada de sistemas operacionais como, por exemplo, UNIX, FreeBSB, Linux, Windows e MacOS.
O R foi criado originalmente por Ross Ihaka e por Robert Gentleman na Universidade de Auckland, Nova Zelândia, e foi desenvolvido por um esforço colaborativo de pessoas em vários locais do mundo. O nome R provém em parte das iniciais dos criadores e também de um jogo figurado com a linguagem S (da Bell Laboratories, antiga AT&T). Resumidamente, então, pode-se dizer que o R é um conjunto integrado de instalações de software para manipulação de dados, cálculos e exibição gráfica que tem como característiscas:
- Instalação de manipulação eficaz e armazenamento de dados,
- Conjunto de operadores para cálculos em matrizes, em particular matrizes,
- Coleta ampla, coerente e integrada de ferramentas intermediárias para análise de dados,
- Instalações gráficas para análise de dados e exibição em tela ou em cópia impressa, e
- Linguagem de programação bem desenvolvida, simples e eficaz, que inclui condicionais, laço de repetiçãos, funções recursivas definidas pelo usuário e instalações de entrada e saída.
2.1 Instalando o R
As etapas a seguir podem ser usadas para instalar o R em um sistema operacional Windows. Para instalar o R em um sistema operacional Macintosh ou Linux, consulte o website: https://cran.rstudio.com.
1º Passo: Acesse RStudio Comprehensive R Archive Network (CRAN) em https://cran.rstudio.com. [Você também pode selecionar um domínio diferente acessando http://www.r-project.org, selecionando o link “download R” na caixa “Getting Started” e selecionando um domínio na página seguinte.]
2º Passo: Na página aberta, selecione o link “Baixar R para Windows”.
- 3º Passo: Selecione a opção “base”.
- 4º Passo: Selecione a opção “Download R 4.2.2 for Windows”. Em seguida, execute o programa ou anote onde você salvou esse programa executável em seu computador.
- 5º Passo: Selecione o idioma “Inglês” na primeira
caixa de diálogo (dependendo da sua versão do Windows, você pode ter
recebido avisos de segurança antes desta caixa de diálogo aparecer).
Pressione Next nas próximas duas caixas de diálogo
(primeiro, uma descrição simples; segundo, um contrato do usuário).
- 6º Passo: Neste ponto, você pode optar por instalar
32 ou 64 bits ou ambas as versões do R. Se você não tiver um computador
de 64 bits, deverá instalar a versão de 32 bits. Se você tiver um
computador de 64 bits, inicialmente e para simplificar, instale apenas
uma versão ou outra. Em seguinda, pressione
Next.
- 7º Passo: Selecione um local para instalar o R
(basta usar o local padrão se o local não for importante para você), e
pressione Avançar. Em seguida, selecione a opção “Não (aceitar padrões)”
(este é o padrão). Decida se deseja criar um atalho na pasta do Menu
Iniciar e se deseja ou não criar ícones de área de trabalho ou Quick
Launch (duas opções principais) e, também, se deseja registrar o número
da versão e associar arquivos .RData com R (duas opções inferiores), e
pressione Next.
- 8º Passo: R deve então começar a instalar arquivos no diretório que você escolheu anteriormente. Se tudo correr bem, você deve obter uma última caixa de diálogo observando isso. Então, basta pressionar Done.
2.2. Interfaces para o R: O RStudio
Ao contrário de muitos outros programas, os usuários interagem com o R por meio de uma linha de comando, e não por meio de uma interface gráfica do usuário. Embora essa interface possa não ser familiar para muitos usuários, sua força principal é a capacidade dos usuários de desenvolver scripts de comandos para realizar várias análises que podem ser facilmente repetidas. Todavia, nos dias atuais, pode-se trabalhar com ambientes de desenvolvimento integrado (IDE) para deixar o uso do R mais amigável. Dentre todas as IDEs disponíveis, a mais popular é o RStudio.
O RStudio é um ambiente de desenvolvimento integrado
(IDE) de código-fonte aberto que serve como um front-end “no topo” do R.
O RStudio facilita a interação do usuário com o R fornecendo algumas das
conveniências de uma GUI e, mais importante, um meio para construindo e
executando scripts R de forma eficiente. Entre outras conveniências, o
RStudio fornece um layout de quatro painéis que inclui um editor de
código-fonte rico em recursos (inclui realce de sintaxe, conclusão de
parênteses, verificação ortográfica etc.), um link estreito para o
console R, um sistema para examinar objetos salvo em R, uma interface
para a ajuda do R e recursos estendidos para examinar e salvar
gráficos.
Para instalar a IDE do RStudio, basta seguir os passos:
1º Passo: Acesse a página de download do RStudio disponível em: https://posit.co/download/rstudio-desktop/#download.
2º Passo: Selecione o link na lista “Instaladores para plataformas suportadas” que corresponde ao sistema operacional apropriado para o seu computador. Execute o programa baixado e siga as instruções da tela.
2.3. Conceitos Básicos do R
2.3.1. Tipos de Estruturas de Dados
Basicamente, há quatro tipos de dados no R: númericos, caracteres, lógicos e números complexos. Cada objeto possui dois atributos: tipo (mode) e o tamanho (length). Além desses atributos, os tais podem ser classificados em certas estruturas conhecidas como vetores, data.frames, array, matrizes e listas. É importante destacar que é preciso ter claro como são dispostas as estruturas de dados no R.
- Vetores (c) - Conjuntos de dados unidimensionais do mesmo tipo.
# Exemplos:
x <- c(1, 4, 10.5, 54.48, 9, 10)
x## [1] 1.00 4.00 10.50 54.48 9.00 10.00
y <- seq(from = 1, to = 10, by = 0.5)
y## [1] 1.0 1.5 2.0 2.5 3.0 3.5 4.0 4.5 5.0 5.5 6.0 6.5 7.0 7.5 8.0
## [16] 8.5 9.0 9.5 10.0
z <- c('Vetor', 'Data.Frame', 'Array', 'Matrizes', 'Listas')
z## [1] "Vetor" "Data.Frame" "Array" "Matrizes" "Listas"
- Matrizes (matrix) - Conjuntos bidimensionais de dados do mesmo tipo. As matrizes não são consideradas agrupamento de vetores.
# Exemplos:
A <- matrix(data = c(1:10), nrow = 2, ncol = 5, byrow = TRUE)
A## [,1] [,2] [,3] [,4] [,5]
## [1,] 1 2 3 4 5
## [2,] 6 7 8 9 10
B <- matrix(data = seq(from = 1, to = 10, by = 1), nrow = 2, ncol = 5, byrow = TRUE)
B## [,1] [,2] [,3] [,4] [,5]
## [1,] 1 2 3 4 5
## [2,] 6 7 8 9 10
- Arrays (array) - Conjunto de dados do mesmo tamanho e tipo de dados. Nos arrays, os elementos individuais são acessados por sua posição que é dada por um índice, chamado de subscrição.
# Exemplo:
ar <- array(c(1:10), dim = c(2,5))
ar## [,1] [,2] [,3] [,4] [,5]
## [1,] 1 3 5 7 9
## [2,] 2 4 6 8 10
- Data.frames (data.frame) - Conjuntos bidimensionais de vetores de mesmo comprimento, sendo que cada vetor pode ser de um tipo diferente. Nos data.frames os vetores são agrupados pelas colunas.
# Exemplo:
A <- matrix(data = c(1:10), nrow = 2, ncol = 5, byrow = TRUE)
db <- data.frame(A)
db## X1 X2 X3 X4 X5
## 1 1 2 3 4 5
## 2 6 7 8 9 10
- Listas (list) - Conjuntos de dados de qualquer tipo, incluindo listas de listas. Cada elemento da lista pode ser considerado um “vetor”.
# Exemplo:
x <- c(1, 4, 10.5, 54.48, 9, 10)
A <- matrix(data = c(1:10), nrow = 2, ncol = 5, byrow = TRUE)
db <- data.frame(A)
lista <- list(x, A, db)
lista## [[1]]
## [1] 1.00 4.00 10.50 54.48 9.00 10.00
##
## [[2]]
## [,1] [,2] [,3] [,4] [,5]
## [1,] 1 2 3 4 5
## [2,] 6 7 8 9 10
##
## [[3]]
## X1 X2 X3 X4 X5
## 1 1 2 3 4 5
## 2 6 7 8 9 10
2.3.2. Comandos do Workspace
Abaixo veremos uma tabela com os principais comandos que ajudam a manipular os objetos e a workspace que estão sendo utilizados durante a execução do programa R.
| Função | Descrição |
| ls() ou objects() | Lista curta de variáveis definidas |
| ls.str() | Lista detalhada de variáveis definidas |
| str(x) | Ver informações detalhadas de x |
| ls.str(ab) | Ver informações detalhadas sobre todas as variáveis com “ab” em seu nome |
| rm(x) | Deletar variável x |
| rm(x, y) | Deletar as variáveis x e y |
| rm(list = ls()) | Deletar todas as variáveis (limpar a workspace) |
| class(x) | Ver que tipo de objeto é x |
| q() | Sair do R com a opção de salvar a workspace em um arquivo (“Name.RData”) |
| setwd() | Define o diretório de dados |
| getwd() | Resgata o diretório de dados |
| download.file() | Realiza download de dados provenientes da internet |
| $ | Operador para selecionar uma variável da base de dados |
| [] | Operador para selecionar uma posição dos dados |
| ctrl + L | No teclado, pressione “ctrl+L” para limpar a tela da console |
2.3.3. Instalando Pacotes
Um pacote do R é uma coleção de funções, dados e documentação que estende as funcionalidades básicas do R, sendo desenvolvidos pela comunidade do R que é formada por diversos contribuidores. Por default, na instalação do R são instalados apenas os pacotes básicos e recomendados para o funcionamento do R. No entanto, é possível instalar pacotes de outras fontes como, por exemplo, GitHub e R-Forge.
Em geral, a forma mais fácil de instalar uma pacote do R é através da
função install.packages("nome_do_pacote"). Por este
comando, o pacote informado é instalado a partir do repositório oficial
de distribuição de pacotes: a (CRAN). A CRAN é uma rede de servidores e
FTP distribuídas pelo mundo e mantida pela comunidade R. É importante
destacar que a Fundação R coordena a CRAN e estabelece diversos testes
para assegurar que os pacotes publicados sigam as políticas da CRAN.
Atualmente, existem mais de 2.000 pacotes disponíveis para download
gratuito do CRAN que passaram por tais políticas.
Para exemplificar a instalação de pacotes do CRAN, instalaremos o
pacote remotes que dispõe de funções para instalar pacotes
de repositórios remotos, como por exemplo do GitHub.
# install.packages("remotes")
# Para ter acesso as funções disponibilizadas com o pacote você precisa carregar o pacote:
# library(remotes)
# Apesar de precisar só instalar uma vez um pacote, você precisará carregá-lo a cada nova sessão.Para desinstalar um pacote você pode usar a função
remove.packages("nome_do_pacote").
2.3.3.1. Pacotes do GitHub e R-forge
Nem todos pacotes são disponíveis na CRAN. Muitos desenvolvedores disponibilizam seus pacotes em plataformas como o GitHub e R-forge. Às vezes um pacote pode estar em ambos CRAN e GitHub (ou R-forge), mas a última versão - a de desenvolvimento - é somente disponibilizada no GitHub (ou R-forge).
Para instalar um pacote de um repositório do GitHub usa-se a função
install_github() do pacote remotes. Portanto,
o pacote remotes precisa ser sido instalado primeiro para
trabalhar com o GitHub. Além disso, a função para instalar um pacote do
GitHub requer como argumento o nome do usuário/nome do repositório. Por
exemplo, para instalar o pacote ADARdata do repositório
mantido pelo lhmet, utiliza-se os comandos:
# library(remotes)
# install_github("lhmet/ADARdata")Você pode acessar uma função de um pacote instalado com a forma especial `pacote::funcao``. Nesse sentido, o trecho de código anterior poderia ser reduzido a:
# remotes::install_github("lhmet/ADARdata")Essa forma deixa explícito que estamos usando a função
install_github() do pacote remotes.
Por outro lado, para instalar um pacote em um repositório do R-forge,
por exemplo, o repositório do pacote raster, utiliza-se os
comandos:
# install.packages("raster",repos = "http://R-Forge.R-project.org")2.3.3.2. Arquivo Local
Uma outra alternativa de instalação de pacotes, é a utilização de arquivos de pacotes locais. Em geral, os códigos fonte de pacotes do R são armazenados como arquivos com a extensão .tar.gz. Binários compilados são armazenados com a extensão .zip. Exemplo de arquivos como estes podem ser baixados manualmente da CRAN (veja a seção Downloads em por exemplo, https://cran.r-project.org/web/packages/remotes/index.html), no GitHub ou R-forge.
Assim, para instalar um pacote a partir desses arquivos localmente,
utiliza-se a função install.packages(), especificando o
argumento repos = NULL e o argumento pkgs com
o caminho do arquivo. Por exemplo:
# install.packages(pkgs = "remotes_2.1.1.tar.gz", repos = NULL)2.3.4. O Comando help
Durante a utilização do software é possível consultar a sintaxe de
algum comando ou obter mais informações sobre determinada função. Para
isso o R conta com o comando help(). A sintaxe do comando é
a seguinte:
# help(comando)
# Exemplo:
# help(sqrt)Ao executar o exemplo acima, uma interface do menu de ajuda será executada mostrando o tópico da função sqrt, que é função matemática para a raiz quadrada. Para realizar uma busca em arquivos de ajuda sobre um tópico desejado, podemos utilizar os seguintes comandos:
# help.search("expressão")
# ??expressão
# Exemplos:
# help.search("negative binomial")
# ??weibullEm relação aos pacotes, cada pacote possui uma página de resumo com uma descrição curta e links para a documentação do pacote. Depois de localizar um pacote potencialmente interessante, você pode clicar no link “Manual de referência” para visualizar a documentação em PDF com todos os detalhes, ou utilizar o comando:
# help(package="packagename")
# Exemplo:
# help(package="tseries")2.3.5. Atribuição de Valores
Como todo tipo de programação (inclusive funcional), é comum que tenhamos que atribuir valores para algumas variáveis antes de utilizá-las (esse processo também é conhecido como inicialização de variáveis). No R podemos fazer uma atribuição de valores de várias formas, conforme os exemplos abaixo:
x <- 10 # x é a variável que recebe o valor 10;
0.56 -> x # x é a variável que recebe o valor 0.56;
x = -8 # x é a variável que recebe o valor -8;
assign("x", 2i) # x é a variável que recebe o imaginário 2i;Na maior parte do tempo utilizaremos os símbolos “<−” e “=” para
atribuição de valores, isto é, variavel <- valor. Em
geral, uma atribuição armazena o valor (no lado direita da atribuição)
em uma variável (no lado esquerdo da atribuição, sendo que a variável é
um nome usado para guardar os dados.
Para visualizar o valor de uma variável, basta digitar o nome da
variável na linha de comando, ou imprimir seu valor com a função
print(). Isto é,
x <- 10 # x é a variável que recebe o valor 10;
x## [1] 10
print(x)## [1] 10
2.3.6. Operações Matemáticas
Os símbolos da linguagem R para realizar as operações matemáticas básicas são construídos através dos operadores usuais e das regras de precedência de qualquer linguagem de edição de dados, isto é,
| 1 | ^ | Potenciação |
| 2 | / | Divisão |
| 3 | * | Multiplicação |
| 4 | + | Adição |
| 5 | - | Subtração |
Além dos operadores matemáticos, o R também possui dois tipos de operadores importantes: os operadores relacionais e operadores lógicos. Os operadores relacionais, permitem estabelecer a relação entre dois valores de entrada, e retornar um valor lógico verdadeiro ou falso dependendo da relação. Por exemplo, um operador de comparação pode comparar dois números e dizer se eles são iguais ou não. Tais operadores são:
| Símbolo | Descrição |
| \(<\) | Menor |
| \(<=\) | Menor ou igual |
| \(>\) | Maior |
| \(>=\) | Maior ou igual |
| \(==\) | Igual (comparação) |
| \(!=\) | Diferente |
É importanto destacar que os operadores de comparação sempre retornam um valor lógico TRUE ou FALSE. Por outro lado, os operadores lógicos, também conhecidos como operadores booleanos, permitem trabalhar com múltiplas condições relacionais na mesma expressão, e retornam valores lógicos verdadeiro ou falso. Tais operadores são:
| Símbolo | Descrição |
| \(\&\) | AND (versão vetorizada) |
| \(\&\&\) | AND (versão não-vetorizada) |
| \(|\) | OR (versão vetorizada) |
| \(||\) | OR (versão não-vetorizada) |
| \(!\) | NOT (negação lógica) |
| xor | XOR (Ou exclusivo) |
| TRUE | Valor booleano verdadeiro |
| FALSE | Valor booleano falso |
2.3.7. Criação de Funções
Em geral, as funções são atribuídas e salvas em objetos, sendo declaradas com a palavra function. Os argumentos, obrigatórios ou opcionais, são definidos na declaração da função entre parênteses. Devido ao fato que normalemnte as funções possuem mais de uma linha de código, os blocos de código da função são especificados entre chaves e a função return() especifica o que é retornado. Em termos de código, uma função é escrita como:
nome <- function(argumento_1, ..., argumento_n)
{
# Corpo / Comandos da função
# return()
}Em outras palavras, toda função tem como elementos básicos:
Nome: Precisamos dar um nome à nossa função, e já devemos ter atenção neste passo. O nome da função deve seguir às mesmas regras de nomeação de variáveis, como, por exemplo, não começar com números. Ao nome de nossa função atribuímos o comando function(). Depois de pronta, a função será executada usando o nome que atribuímos.
Argumentos da função: Devemos pensar em quais informações nossa função irá precisar, que chamamos de argumentos da função. Se queremos uma função que calcule a média de um vetor, por exemplo, precisamos do vetor ou do somatório dos elementos do vetor e do comprimento do mesmo. Esses argumentos são colocados dentro do comando function() separados por vírgula.
Corpo/Comandos da função: Agora entra o código que faz o que a função necessita. Dependendo do objetivo da função teremos que usar estruturas de controle, como condições ou laço de repetiçãos, ou poderemos usar recursão. Basicamente todos os comandos que já conhecemos podem estar nesta parte. O importante é que todos esses comandos que fazem parte da função devem estar delimitados. Precisamos especificar onde começa e onde acaba a nossa função, assim como fazemos com laço de repetiçãos e com o if(), por exemplo.
return(): Um comando não obrigatório, mas que é bastante comum no final das funções é o return(). Nossa função vai obter algum resultado e este comando faz com que o R retorne o objeto dentro dos parênteses de return() no console.
Por outro lado, as funções também podem ser definidas minimamente. Essa definição mínima é viável apenas para funções com apenas uma linha de código, neste caso, não são usadas nem as chaves ({}) para especificar o bloco de códigos nem a função return, já que apenas o objeto resultante da única linhas de código é retornado. Por exemplo, podemos trabalhar com as seguintes estruturas de código:
# Atribuição de uma típica função
funcao.tipica <- function(x)
{
result <- x+1
return(result)
}
# Função mímina, apenas com os elementos essenciais
funcao.minima <- function(x) x+1A primeira coisa que deve ser observada é que funções não se limitam a funções matemáticas: uma função recebe alguns inputs, faz algumas operações e devolve um output. Vale lembrar que as funções são criadas para tarefas que serão repetidas várias vezes, sendo assim, não faz nenhum sentido escrever uma função que só será usada uma única vez. Em geral, escrever a função exige, de antemão, que você saiba o tamanho dos vetores, matrizes, como o código irá se comportar, etc.
2.3.8. Laços de Repetição
“Looping”, “cycling”, “iterating” ou apenas replicação de instruções é uma prática antiga que se originou bem antes da invenção dos computadores. Nada mais é do que automatizar um processo de várias etapas, organizando sequências de ações ou processos “em lote” e agrupando as partes que precisam ser repetidas. Todas as linguagens modernas de programação fornecem construções especiais que permitem a repetição de instruções ou blocos de instruções.
De um modo geral, existem dois tipos dessas construções ou laços de
repetição especiais nas linguagens de programação modernas. Alguns laços
são executados por um determinado número de vezes, conforme controlado
por um contador ou índice, incrementado a cada ciclo de iteração. Estes
fazem parte da família de laços de repetição for. Por outro
lado, alguns laços baseiam-se no início e na verificação de uma condição
lógica. A condição é testada no início ou no fim da construção do laço.
Estas variantes pertencem à família while ou
repeat, respectivamente.
2.3.8.1. Laços de Repetição for
A estrutura de repetição for permite criar loops para
casos onde sabemos de antemão o número de repetições que devem ser
realizadas. Para exemplificar, vamos supor que devemos imprimir os
números de 1 até 10. Para resolver esses problema, podemos executar o
comando print() dez vezes, no entanto, isso se tornaria
inviável se em vez de 1 até 10, estivéssemos interessados em imprimir os
números de 1 até 100. Então, como alternativa a esse processo, podemos
trabalar com o laço de repetição for, isto é,
# Estrutura do laço for:
# for (variável in vetor_números)
# {
# Corpo do laço
# }
for (i in 1:10)
{
print(i)
}## [1] 1
## [1] 2
## [1] 3
## [1] 4
## [1] 5
## [1] 6
## [1] 7
## [1] 8
## [1] 9
## [1] 10
2.3.8.2. Laços de Repetição while
A estrutura while realiza um teste lógico no início do
laço, e cada vez que esse teste retorna o valor verdadeiro, os comandos
associados ao laço são executados. Quando o teste retornar o valor
falso, o laço é interrompido. Para exemplificar, vamos considerar a
seguinte rotina:
# Estrutura do laço while:
# while(teste)
# {
# Corpo do laço
# }
x <- 1
while(x <= 10)
{
print(x)
x <- x + 1
}## [1] 1
## [1] 2
## [1] 3
## [1] 4
## [1] 5
## [1] 6
## [1] 7
## [1] 8
## [1] 9
## [1] 10
2.3.8.3. Laços de Repetição repeat
A estrutura repeat faz com que o R repita o código a
seguir sem condições. Com isso, precisamos de mais uma função para
mostrar ao programa quando deve parar de repetir o código, chamada de
break(). Como sempre existe uma condição a ser satisfeita
para o código continuar a ser repetido ou parar, então devemos também
usar a função if(). Para exemplificar, vamos considerar a
seguinte rotina:
# Estrutura do laço repeat:
# repeat
# {
# Comandos a serem repetidos
# if (Condição para que a repetição pare) break()
# }
j <- 1
repeat
{
print(j)
j <- j+1
if (j > 10) break()
}## [1] 1
## [1] 2
## [1] 3
## [1] 4
## [1] 5
## [1] 6
## [1] 7
## [1] 8
## [1] 9
## [1] 10
2.3.9. Família apply
As funções da família apply são alternativas aos laço de
repetição/laços, elas pertencem ao pacote base do R e são projetadas
para serem aplicadas a diferentes estruturas de dados como vetores,
data.frames, matrizes e listas. As funções permitem cruzar os dados de
várias maneiras e evitar, assim, o uso explícito de laço de
repetição/laços. Essas funções funcionam de maneira similar, todas
requerem uma entradas de dados organizada em uma das estruturas básicas
e aplicam uma segunda função a estrutura, sendo possível passar
argumentos opcionais para a função aplicada. Os resultados da aplicação
podem ser simples, como estátisticas descritivas e transformações ou
mesmo retornar estruturas complexas como matrizes ou listas.
De uma maneira ampla, as funções desta família ajudam a executar
operações com poucas linhas de código, e é composta principalmente por
apply, sapply, lapply,
tapply e mapply, e funções associadas que
compartilham da mesma lógica como sweep,
replicate, entre outras. Para exemplificar o uso da família
apply, considere um banco de dados relativo as medidas das
pétalas e sépalas de certas espécies de plantas disponível no software
R. Este banco de dados será referido como iris.
# Carregar os dados
data(iris)
# Visualização dos dados
head(iris, n = 10)## Sepal.Length Sepal.Width Petal.Length Petal.Width Species
## 1 5.1 3.5 1.4 0.2 setosa
## 2 4.9 3.0 1.4 0.2 setosa
## 3 4.7 3.2 1.3 0.2 setosa
## 4 4.6 3.1 1.5 0.2 setosa
## 5 5.0 3.6 1.4 0.2 setosa
## 6 5.4 3.9 1.7 0.4 setosa
## 7 4.6 3.4 1.4 0.3 setosa
## 8 5.0 3.4 1.5 0.2 setosa
## 9 4.4 2.9 1.4 0.2 setosa
## 10 4.9 3.1 1.5 0.1 setosa
2.3.9.1. Função apply
A função apply aplica funções nas margens de matrizes. É
preciso definir qual das margem da matriz a função será aplicada: se
MARGIN = 1 a função é aplicada nas linhas, se
MARGIN = 2será aplicada nas colunas, ainda, se
MARGIN = c(1,2) a função é aplicada das linhas e colunas.
Além disso, é preciso difinir qual função será aplicada usando o
argumento FUN e quaisquer outros argumentos da função a ser aplicada
devem ser passados pelo argumento. Para exemplificar, vamos trabalhar
com a soma de todos os valores das colunas da nossa base de dados
iris, isto é,
# Uso da função apply():
# apply(X, MARGIN, FUN, ...)
apply(X = iris[,1:4], MARGIN = 2, FUN = sum)## Sepal.Length Sepal.Width Petal.Length Petal.Width
## 876.5 458.6 563.7 179.9
2.3.9.2. Função tapply
A função tapply divide as estruturas de dados e aplica
funções a cada subconjunto, geralmente as funções passada calculam
estatísticas descritivas. O argumento INDEX especifica um
ou mais fatores para dividir os elementos da estrutura. O funcionamento
dessa função é similar as funções aggregate e
by. Para ilustrar melhor esta função, vamos considerar
agora a variável Species do nosso banco de dados
iris. Suponha que nosso objetivo seja calcular a soma de
todos os valores dos tamanhos de sépala (variável
Sepal.Length) de acordo com a espécie de planta (variável
Species). Neste caso, então, temos:
# Uso da função tapply():
# tapply(X, INDEX, FUN = NULL, ..., default = NA, simplify = TRUE)
tapply(iris$Sepal.Length, INDEX = iris$Species, FUN = sum)## setosa versicolor virginica
## 250.3 296.8 329.4
2.3.9.3. Função sapply
A função sapply aplica funções a cada elemento de um
vetor, pode ser aplicada em vetores unidimensionais, data.frames
(agrupamentos de vetores) e listas (também considerados vetores). Por
padrão a função simplifica os resultados se possível, então pode
retornar tanto único vetor, uma matriz ou listas. O argumento
simplify determina o comportamento da simplificação, sendo
que quando simplify = FALSE sempre é retornado uma lista.
Para ilustrar melhor esta função, suponha que nosso objetivo seja
calcular a soma de todos os valores das variáveis numéricas da nossa
base de dados iris. Neste caso, então, temos:
# Uso da função sapply():
# sapply(X, FUN, ..., simplify = TRUE, USE.NAMES = TRUE)
sapply(iris[,1:4], FUN = sum, simplify = FALSE)## $Sepal.Length
## [1] 876.5
##
## $Sepal.Width
## [1] 458.6
##
## $Petal.Length
## [1] 563.7
##
## $Petal.Width
## [1] 179.9
2.3.9.4. Função lapply
A função lapply funciona praticamente igual à
sapply e permite a aplicação de uma função em cada elemento
de um vetor unidimensional, data.frame ou lista. A principal diferença
entre as elas é que a função lapply sempre retorna uma
lista com os resultados. É preferível manter a função
sapply na aplicação de vetores e lapply para
aplicação de listas. Para ilustrar melhor esta função, suponha,
novamente, que nosso objetivo seja calcular a soma de todos os valores
das variáveis numéricas da nossa base de dados iris. Neste
caso, então, temos:
# Uso da função lapply():
# lapply(X, FUN, ...)
lapply(iris[,1:4], FUN = sum)## $Sepal.Length
## [1] 876.5
##
## $Sepal.Width
## [1] 458.6
##
## $Petal.Length
## [1] 563.7
##
## $Petal.Width
## [1] 179.9
2.4. Importação de Dados
Dados para análise estatística podem estar em diversos formatos (tipos de arquivos). O usuário poderia, por exemplo, ter criado um arquivo num software de planilha eletrônica, ou no próprio pacote estatístico que será utilizado. Alguns dos formatos mais usados são mostrados a seguir:
- Planilha eletrônica (.xls, .xlsx, .ods)
- Valores separados por vírgulas (.csv)
- Dbase (.dbf)
- Diversos Pacotes estatísticos: (.Rdata - R), (.sav - SPSS), etc
No geral, as funções disponíveis no R para importação de dados são:
read.csv(): para leitura de arquivos “valores separados por vírgula” (“.csv”).read.csv2(): variante utilizada em países que utilizam vírgula “,” como ponto decimal e ponto e vírgula “;” como separadores de campo.read.delim(): para leitura de arquivos “tab-separated value” (“.txt”). Por padrão, o ponto (“.”) é usado como pontos decimais.read.delim2(): para leitura de arquivos “tab-separated value” (“.txt”). Por padrão, a vírgula (“,”) é usada como pontos decimais.
A sintaxe simplificada dessas funções são definidas comos:
- Leitura de arquivos delimitados por tabulações, forma geral:
- read.table(file, header = FALSE, set = ““, dec =”.”)
- read.table(file, header = FALSE, set = ““, dec =”.”)
- Leitura de arquivos de “valores separados por vírgula” (“.csv”):
- read.csv(file, header = TRUE, set = “,”, dec = “.”, …)
- read.csv2(file, header = TRUE, set = “;”, dec = “,”, …)
- Leitura arquivos delimitados por tabulações, forma específica:
- read.delim(file, header = TRUE, set = “, dec =”.”, …)
- read.delim2(file, header = TRUE, set = “, dec =”,“, …)
Em todas elas, os argumentos são:
file: o caminho para o arquivo que contém os dados a serem importados para o R.sep: o caractere separador de campo. “ é usado para arquivo delimitado por tabulações.header: valor lógico. SeTRUE,read.table()assume que seu arquivo tem uma linha de cabeçalho, então a linha 1 é o nome de cada coluna. Se não for o caso, você pode definir esse argumento comoFALSE.dec: o caractere usado no arquivo para pontos decimais.
Para exemplificar, nesta seção, vamos mostrar como importar para o R
de um arquivo de dados do .csv por meio da função
read.csv(). Neste caso, podemos seguir a seguinte
rotina:
# Definir o diretório onde se encontram os dados
setwd('datasets')
# Leitura dos dados pela função read.csv():
# read.csv(file, header = TRUE, sep = ",", quote = "\"",
# dec = ".", fill = TRUE, comment.char = "", ...)
db.heart <- read.csv('heart.csv', header = TRUE, sep = ',')
# Visualização dos dados
head(db.heart, n = 10)## Time Event Gender Smoking Diabetes BP Anaemia Age Ejection.Fraction Sodium
## 1 97 0 0 0 0 0 1 43 50 135
## 2 180 0 1 1 1 0 1 73 30 142
## 3 31 1 1 1 0 1 0 70 20 134
## 4 87 0 1 0 0 0 1 65 25 141
## 5 113 0 1 0 0 0 0 64 60 137
## 6 10 1 1 0 0 0 1 75 15 137
## 7 250 0 1 1 0 0 0 70 40 136
## 8 27 1 1 0 1 1 0 94 38 134
## 9 87 0 1 0 0 1 0 75 45 137
## 10 87 0 1 1 0 0 0 80 25 144
## Creatinine Pletelets CPK
## 1 1.30 237000 358
## 2 1.18 160000 231
## 3 1.83 263358 582
## 4 1.10 298000 305
## 5 1.00 242000 1610
## 6 1.20 127000 246
## 7 2.70 51000 582
## 8 1.83 263358 582
## 9 1.18 263358 582
## 10 1.10 149000 898
# Estrutura dos dados
str(db.heart)## 'data.frame': 299 obs. of 13 variables:
## $ Time : int 97 180 31 87 113 10 250 27 87 87 ...
## $ Event : int 0 0 1 0 0 1 0 1 0 0 ...
## $ Gender : int 0 1 1 1 1 1 1 1 1 1 ...
## $ Smoking : int 0 1 1 0 0 0 1 0 0 1 ...
## $ Diabetes : int 0 1 0 0 0 0 0 1 0 0 ...
## $ BP : int 0 0 1 0 0 0 0 1 1 0 ...
## $ Anaemia : int 1 1 0 1 0 1 0 0 0 0 ...
## $ Age : num 43 73 70 65 64 75 70 94 75 80 ...
## $ Ejection.Fraction: int 50 30 20 25 60 15 40 38 45 25 ...
## $ Sodium : int 135 142 134 141 137 137 136 134 137 144 ...
## $ Creatinine : num 1.3 1.18 1.83 1.1 1 1.2 2.7 1.83 1.18 1.1 ...
## $ Pletelets : num 237000 160000 263358 298000 242000 ...
## $ CPK : int 358 231 582 305 1610 246 582 582 582 898 ...
O código R acima assume que o arquivo “heart.csv” está em seu
diretório de trabalho atual. No entanto, caso o diretório onde se
encontra os dados não seja seja derfinido, é possível fazer a importação
dos dados escolhendo um arquivo de forma interativa usando a função
file.choose(). Neste caso, partimos da seguinte rotina no
R:
## Leitura de um arquivo .txt
# meus_dados <- read.delim(file.choose())
## Leitura de um arquivo .csv
# meus_dados <- read.csv(file.choose())
## Em ambos os casos, você será solicitado a escolher um arquivo.2.4.1. Planilhas do Excel
Em relação a planilhas do Excel, existem alguns pacotes disponíveis
que podem ler dados diretamente de planilhas do MS Excel. No entanto,
estes pacotes geralmente possuem particularidades quanto ao sistema
operacional e demais dependências para funcionar corretamente. Um destes
pacotes, é o readxl, desenvolvido por Hadley Wickman, que
funciona em diversos sistemas operacionais sem necessitar de
dependências externas. Para exemplificar, vamos fazer a importação do
conjuto de dados Leukocyte_Profiles armazenado em uma
planilha do Excel utilizando o readxl, isto é,
# Definir o diretório onde se encontram os dados
setwd('datasets')
# Instalação e leitura do pacote
# install.packages("readxl")
library("readxl")## Warning: package 'readxl' was built under R version 4.2.2
# Leitura dos dados pela função read_excel():
# read_excel(path, sheet = NULL,range = NULL, col_names = TRUE,
# col_types = NULL, na = "", trim_ws = TRUE, skip = 0,
# n_max = Inf, guess_max = min(1000, n_max),
# progress = readxl_progress(), .name_repair = "unique")
db.leukocyte <- read_excel('Leukocyte_Profiles.xlsx')
# Visualização dos dados
head(db.leukocyte, n = 10)## # A tibble: 10 × 9
## Species Fat s…¹ Body …² Heter…³ Lymph…⁴ Eosin…⁵ Monoc…⁶ Basop…⁷ H/L r…⁸
## <chr> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
## 1 Actitis hypo… 5 56 64 19 5 0 12 3.37
## 2 Actitis hypo… 1 44 60 34 0 1 5 1.76
## 3 Actitis hypo… 2 46 63 28 5 0 4 2.25
## 4 Actitis hypo… 3 47 2 87 4 2 5 0.0230
## 5 Actitis hypo… 3 44 68 24 6 0 2 2.83
## 6 Actitis hypo… 5 45 42 45 6 3 4 0.933
## 7 Actitis hypo… 3 44 43 42 6 0 9 1.02
## 8 Actitis hypo… 3 39 40 37 5 1 17 1.08
## 9 Actitis hypo… 3 44 39 40 18 1 2 0.975
## 10 Actitis hypo… 3 49 67 25 2 3 3 2.68
## # … with abbreviated variable names ¹`Fat score`, ²`Body mass (g)`,
## # ³`Heterophils (H)`, ⁴`Lymphocytes (L)`, ⁵Eosinophils, ⁶Monocytes,
## # ⁷Basophils, ⁸`H/L ratio`
# Estrutura dos dados
str(db.leukocyte)## tibble [415 × 9] (S3: tbl_df/tbl/data.frame)
## $ Species : chr [1:415] "Actitis hypoleucos" "Actitis hypoleucos" "Actitis hypoleucos" "Actitis hypoleucos" ...
## $ Fat score : num [1:415] 5 1 2 3 3 5 3 3 3 3 ...
## $ Body mass (g) : num [1:415] 56 44 46 47 44 45 44 39 44 49 ...
## $ Heterophils (H): num [1:415] 64 60 63 2 68 42 43 40 39 67 ...
## $ Lymphocytes (L): num [1:415] 19 34 28 87 24 45 42 37 40 25 ...
## $ Eosinophils : num [1:415] 5 0 5 4 6 6 6 5 18 2 ...
## $ Monocytes : num [1:415] 0 1 0 2 0 3 0 1 1 3 ...
## $ Basophils : num [1:415] 12 5 4 5 2 4 9 17 2 3 ...
## $ H/L ratio : num [1:415] 3.368 1.765 2.25 0.023 2.833 ...
2.5. Exportação de Dados
Importar dados em R é certamente importante para o usuário. No entanto, exportar dados do R para outras plataformas também é igualmente importante. Por exemplo, você pode querer exportar os dados do workspace do R para um arquivo Excel, CSV, arquivo de texto ou, até mesmo, PDF.
A principal função para exportar dados é a função
write.table(). Assim como a função
read.table(), a função write.table() é muito
flexível com muitos argumentos para ajudar a personalizar seu
comportamento. Como exemplo, vamos considerar uma base de dados, chamada
flowers, disponível no link: https://alexd106.github.io/intro2R/data.html. Para
iniciar, então, vamos importar os dados e ordenar as linhas da base de
dados em ordem crescente de altura dentro de cada nível de nitrogênio.
Isto é,
# Definir o diretório onde se encontram os dados
setwd('datasets')
# Leitura dos dados pela função read.csv():
# read.csv(file, header = TRUE, sep = ",", quote = "\"",
# dec = ".", fill = TRUE, comment.char = "", ...)
flowers <- read.csv('flower.csv', header = TRUE, sep = ',')
# Visualização dos dados
head(flowers, n = 10)## treat nitrogen block height weight leafarea shootarea flowers
## 1 tip medium 1 7.5 7.62 11.7 31.9 1
## 2 tip medium 1 10.7 12.14 14.1 46.0 10
## 3 tip medium 1 11.2 12.76 7.1 66.7 10
## 4 tip medium 1 10.4 8.78 11.9 20.3 1
## 5 tip medium 1 10.4 13.58 14.5 26.9 4
## 6 tip medium 1 9.8 10.08 12.2 72.7 9
## 7 tip medium 1 6.9 10.11 13.2 43.1 7
## 8 tip medium 1 9.4 10.28 14.0 28.5 6
## 9 tip medium 2 10.4 10.48 10.5 57.8 5
## 10 tip medium 2 12.3 13.48 16.1 36.9 8
# Ordenação dos dados
flowers_df2 <- flowers[order(flowers$nitrogen, flowers$height), ]
head(flowers_df2)## treat nitrogen block height weight leafarea shootarea flowers
## 68 notip high 1 1.2 18.24 16.6 148.1 7
## 72 notip high 1 2.1 19.15 15.6 176.7 6
## 69 notip high 1 2.6 16.57 17.1 141.1 3
## 76 notip high 2 2.6 18.88 16.4 181.5 14
## 79 notip high 2 4.6 14.65 16.7 91.7 11
## 73 notip high 2 4.7 13.42 19.8 124.7 5
Agora, vamos aplicar uma transformação de raiz quadrada na variável
número de flores (variável flowers) e uma transformação
log10 na variável altura (variável height), salvando essas
transformações em colunas adicionais na nossa base de dados. Isto é,
# Transformações:
flowers_df2$flowers_sqrt <- sqrt(flowers_df2$flowers) # Raiz Quadrada
flowers_df2$log10_height <- log10(flowers_df2$height) # Log10
# Estrutura dos dados:
str(flowers_df2)## 'data.frame': 96 obs. of 10 variables:
## $ treat : chr "notip" "notip" "notip" "notip" ...
## $ nitrogen : chr "high" "high" "high" "high" ...
## $ block : int 1 1 1 2 2 2 2 2 2 2 ...
## $ height : num 1.2 2.1 2.6 2.6 4.6 4.7 5 5.2 6 6.2 ...
## $ weight : num 18.2 19.1 16.6 18.9 14.6 ...
## $ leafarea : num 16.6 15.6 17.1 16.4 16.7 19.8 17.3 19.1 16.2 11.6 ...
## $ shootarea : num 148.1 176.7 141.1 181.5 91.7 ...
## $ flowers : int 7 6 3 14 11 5 15 8 2 5 ...
## $ flowers_sqrt: num 2.65 2.45 1.73 3.74 3.32 ...
## $ log10_height: num 0.0792 0.3222 0.415 0.415 0.6628 ...
Agora podemos exportar nosso novo banco de dados, denominado de
flowers_df2 usando a função write.table().
Neste caso, o primeiro argumento é a base de dados que você deseja
exportar (flowers_df2 em nosso exemplo). O segundo
argumento é o nome do arquivo (com a extensão do arquivo) e o caminho do
arquivo entre aspas simples ou duplas (file = 'caminho').
Como terceiro argumento, tem-se o argumento
col.names = TRUE que indica que os nomes das variáveis
devem ser escritos na primeira linha do arquivo e, como quarto
argumento, o argumento row.names = FALSE que, neste caso,
impede R de incluir os nomes das linhas na primeira coluna do arquivo.
Por fim, o último argumento, o argumento sep = ";" indica
que o separador/delimitador que deve ser utilizado no arquivo exportado
(no nosso caso, iremos trabalhar com “;”). Em outras palavras,
trabalhamos com a seguinte rotina em R:
## Exportação de dados
# Uso da função 'write.table()'
# write.table(x, file = "", append = FALSE, quote = TRUE, sep = " ",
# eol = "\n", na = "NA", dec = ".", row.names = TRUE,
# col.names = TRUE, qmethod = c("escape", "double"),
# fileEncoding = "")
write.table(flowers_df2, file = 'datasets/flowers_df2.txt', col.names = TRUE, row.names = FALSE, sep = "\t")3. Análise Exploratória de Dados
3.1. Introdução
Somos frequentemente lembrados do fato que estamos vivendo na era da informação. Sendo assim, nosso foco, é entender como as informações são obtidas, como são analisadas, e como são interpretadas. Na literatura, dá-se o nome de Estatística à ciência que estuda dados e informações. Basicamente, nossos objetivos são dois: (1) entender as principais ferramentas para organizar e resumir dados (Estatística Descritiva), e (2) entender as principais ferramentas para o auxilio de tomada de decisões sobre um grande volume de dados (Inferência Estatística).
Para recapitular brevemente, a Estatística Descritiva (no sentido amplo do termo) é um ramo da estatística que tem por objetivo resumir, descrever e apresentar uma série de valores ou um conjunto de dados, sendo frequentemente o primeiro passo e uma parte importante em qualquer análise estatística, pois permite verificar a qualidade dos dados e ajuda a “entender” os dados, tendo uma visão geral clara dos mesmos. Se bem apresentada, a estatística descritiva já é um bom ponto de partida para futuras análises.
3.2. Distribuição de Frequências
Quando trabalhamos com dados, em geral, não desejamos trabalhar com os dados brutos, pois as dimensões podem ser gigantescas. Neste caso, podemos trabalhar com a distribuição de frequências que é uma ferramenta útil para resumir tais dados, sejam eles qualitativos ou quantitativos. Neste contexto, para construir a distribuição de frequências completa dos dados, temos cinco conceitos estatísticos importantes: frequência absoluta, frequência absoluta acumulada, frequência relativa, frequência relativa em percentual, e frequência relativa acumulada.
3.2.1. Variáveis Qualitativas
A frequência absoluta nada mais que os valores observados de uma determinada categoria. Essa frequência é denotada por \(f_a\). Já a frequência absoluta acumulada \((F_a)\) é obtida somando-se a frequência absoluta do valor considerado, às frequências absolutas anteriores a este mesmo valor. Por outro lado, a frequência relativa, indica a proporção de observações pertencentes a uma classe em relação ao total \(n\), e é determinada pela equação:
\[f_r = \dfrac{f_a}{n}\] Já frequência relativa em percentual \((f_r(\%))\) representa o percentual de observações a uma classe é obtida simplesmente multiplicando-se a frequência relativa por 100. Por fim, a frequência relativa acumulada \((F_r)\) é obtida da mesma forma que a frequência absoluta acumulada.
Para trabalhar com tabelas de frequência no R, podemos fazer o uso do
pacote summarytools através da função freq().
A função freq() produz tabelas de frequência com
frequências, proporções, bem como informações de dados ausentes. Para
exemplificar o funcionamento da função, vamos considerar a base de dados
heart descrita anteriormente.
## Leitura dos Dados (Dataset: Heart)
setwd('datasets')
db <- read.csv('heart.csv', header = TRUE, sep = ',')
## Reestruturação do Banco de Dados
db$Event <- ifelse(db$Event == 1, 'Non-Censored', 'Censored')
db$Gender <- ifelse(db$Gender == 1, 'Male', 'Female')
db$Smoking <- ifelse(db$Smoking == 1, 'Yes', 'No')
db$Diabetes <- ifelse(db$Diabetes == 1, 'Yes', 'No')
db$BP <- ifelse(db$BP == 1, 'Yes', 'No')
db$Anaemia <- ifelse(db$Anaemia == 1, 'Yes', 'No')
# Visualização dos dados
head(db, n = 10)## Time Event Gender Smoking Diabetes BP Anaemia Age Ejection.Fraction
## 1 97 Censored Female No No No Yes 43 50
## 2 180 Censored Male Yes Yes No Yes 73 30
## 3 31 Non-Censored Male Yes No Yes No 70 20
## 4 87 Censored Male No No No Yes 65 25
## 5 113 Censored Male No No No No 64 60
## 6 10 Non-Censored Male No No No Yes 75 15
## 7 250 Censored Male Yes No No No 70 40
## 8 27 Non-Censored Male No Yes Yes No 94 38
## 9 87 Censored Male No No Yes No 75 45
## 10 87 Censored Male Yes No No No 80 25
## Sodium Creatinine Pletelets CPK
## 1 135 1.30 237000 358
## 2 142 1.18 160000 231
## 3 134 1.83 263358 582
## 4 141 1.10 298000 305
## 5 137 1.00 242000 1610
## 6 137 1.20 127000 246
## 7 136 2.70 51000 582
## 8 134 1.83 263358 582
## 9 137 1.18 263358 582
## 10 144 1.10 149000 898
# Instalação e leitura do pacote
# install.packages("summarytools")
library(summarytools)
# Uso da função
# freq(x, var = NULL, round.digits = st_options("round.digits"), order = "default",
# style = st_options("style"), plain.ascii = st_options("plain.ascii"), justify = "default",
# cumul = st_options("freq.cumul"), totals = st_options("freq.totals"),
# report.nas = st_options("freq.report.nas"), rows = numeric(), missing = "",
# display.type = TRUE, display.labels = st_options("display.labels"),
# headings = st_options("headings"), weights = NA, rescale.weights = FALSE, ...)
freq(db$Smoking)## Frequencies
## db$Smoking
## Type: Character
##
## Freq % Valid % Valid Cum. % Total % Total Cum.
## ----------- ------ --------- -------------- --------- --------------
## No 203 67.89 67.89 67.89 67.89
## Yes 96 32.11 100.00 32.11 100.00
## <NA> 0 0.00 100.00
## Total 299 100.00 100.00 100.00 100.00
Uma outra opção é fazer o uso do pacote epiDisplay
através da função tab1(). A função tab1() é
uma tabulação unidirecional avançada que fornece uma boa tabela de
frequências e também um gráfico de barras. A descrição da variável
também é usada no título principal do gráfico.
# Instalação e leitura do pacote
# install.packages("epiDisplay")
library(epiDisplay)## Warning: package 'epiDisplay' was built under R version 4.2.2
## Loading required package: foreign
## Loading required package: survival
## Loading required package: MASS
## Loading required package: nnet
# Uso da Função:
# tab1(x0, decimal = 1, sort.group = FALSE, cum.percent = !any(is.na(x0)), graph = TRUE, missing = TRUE,
# bar.values = "frequency", horiz = FALSE, cex = 1, cex.names = 1, main = "auto", xlab = "auto",
# ylab = "auto", col = "auto", gen.ind.vars = FALSE, ...)
tab1(db$Smoking, sort.group = "decreasing", cum.percent = TRUE, main = 'Distribuição de Frequências: Fumo')## db$Smoking :
## Frequency Percent Cum. percent
## No 203 67.9 67.9
## Yes 96 32.1 100.0
## Total 299 100.0 100.0
3.2.2. Variáveis Quantitativas
Vamos supor, agora, que nossos dados sejam de natureza quantitativa contínua em vez de natureza qualitativa. Como a natureza dos dados agora é contínua, devemos trabalhar com intervalos de classe (transformando-os em categorias) e, então, utiliza-se da distribuição de frequências. Para classificar, então, os dados de natureza contínua em intervalos de classe, primeiramente, deve-se definir o número de classes que será utilizado. Como regra geral, é recomendado utilizar entre 5 a 20 classes dependendo da magnitude dos dados. Em algumas situações, no entanto, pode-se trabalhar com uma metodologia conhecida como Regra de Sturges para definir o número de classes. A regra de Sturges é descrita pela equação:
\[k = 1 + 3.3 \cdot \log(n)\]
em que \(k\) é o número total de classes e \(n\) é o tamanho da amostra. Uma vez definido o número de classes, precisamos também definir à amplitude da classe que nos diz o quão grande deve ser nosso intervalo de classe. Tal amplitude é descrita, matematicamente, por:
\[A =\dfrac{\text{Maior valor observado} - \text{Menor valor observado}}{k}\]
Agora, conhecendo o número e à amplitude da classe, precisamos definir o que chamamos de limites de classe. Os limites da classe devem ser escolhidos de modo que cada observação pertença a uma e apenas uma classe, e são divididos em: limite inferior \((l_i)\), e limite superior \((L_i)\).
Para trabalhar com tabelas de frequência para variáveis quantitativas
no R, pode-se trabalhar com o pacote agricolae
utilizando-se as funções graph.freq() e
table.freq() que criam os intervalos de classe com base na
fórmula de Sturges. Para exemplificar o funcionamento da função, vamos
considerar a base de dados growth disponível no pacote
agricolae do R.
# Instalação e leitura do pacote
# install.packages("agricolae")
library(agricolae)## Warning: package 'agricolae' was built under R version 4.2.2
# Leitura dos dados
data(growth)
db.growth <- growth
# Visualização dos dados
head(db.growth, n = 10)## place slime height
## 1 L1 6 9.4
## 2 L1 7 9.2
## 3 L1 7 10.2
## 4 L1 6 12.5
## 5 L1 10 12.2
## 6 L1 9 10.5
## 7 L1 17 12.1
## 8 L1 17 10.2
## 9 L1 17 11.4
## 10 L1 18 10.1
# Uso da função graph.freq()
# graph.freq(x, breaks=NULL,counts=NULL,frequency=1, plot=TRUE, nclass=NULL,
# xlab="",ylab="",axes = "",las=1,...)
tab <- with(growth, graph.freq(height, plot = FALSE))
# Uso da função table.freq()
# table.freq(object)
print(table.freq(tab),row.names=FALSE)## Lower Upper Main Frequency Percentage CF CPF
## 6.0 7.2 6.6 1 3.3 1 3.3
## 7.2 8.4 7.8 0 0.0 1 3.3
## 8.4 9.6 9.0 4 13.3 5 16.7
## 9.6 10.8 10.2 8 26.7 13 43.3
## 10.8 12.0 11.4 11 36.7 24 80.0
## 12.0 13.2 12.6 6 20.0 30 100.0
3.2.3. Múltiplas Tabelas de Frequência: Uso de Laço de Repetição
Em muitas situações, trabalhar com tabelas de frequências pode ser um pouco extenso quando há mais de uma variável para construir as mesmas. Nestes casos, com o objetivo de facilitar o trabalho, podemos, a partir do conceito de laço de repetição e a estrutura de dados de lista, construir as tabelas de frequência para todas as variáveis de uma única vez, em vez de uma a uma como normalmente se faz. Sendo assim, para o caso das variáveis qualitativas, seguimos a rotina:
## Leitura dos Dados (Dataset: Heart)
setwd('datasets')
db <- read.csv('heart.csv', header = TRUE, sep = ',')
head(db) # Visuzalição dos dados## Time Event Gender Smoking Diabetes BP Anaemia Age Ejection.Fraction Sodium
## 1 97 0 0 0 0 0 1 43 50 135
## 2 180 0 1 1 1 0 1 73 30 142
## 3 31 1 1 1 0 1 0 70 20 134
## 4 87 0 1 0 0 0 1 65 25 141
## 5 113 0 1 0 0 0 0 64 60 137
## 6 10 1 1 0 0 0 1 75 15 137
## Creatinine Pletelets CPK
## 1 1.30 237000 358
## 2 1.18 160000 231
## 3 1.83 263358 582
## 4 1.10 298000 305
## 5 1.00 242000 1610
## 6 1.20 127000 246
dim(db) # Dimensão dos dados## [1] 299 13
str(db) # Estrutura dos dados## 'data.frame': 299 obs. of 13 variables:
## $ Time : int 97 180 31 87 113 10 250 27 87 87 ...
## $ Event : int 0 0 1 0 0 1 0 1 0 0 ...
## $ Gender : int 0 1 1 1 1 1 1 1 1 1 ...
## $ Smoking : int 0 1 1 0 0 0 1 0 0 1 ...
## $ Diabetes : int 0 1 0 0 0 0 0 1 0 0 ...
## $ BP : int 0 0 1 0 0 0 0 1 1 0 ...
## $ Anaemia : int 1 1 0 1 0 1 0 0 0 0 ...
## $ Age : num 43 73 70 65 64 75 70 94 75 80 ...
## $ Ejection.Fraction: int 50 30 20 25 60 15 40 38 45 25 ...
## $ Sodium : int 135 142 134 141 137 137 136 134 137 144 ...
## $ Creatinine : num 1.3 1.18 1.83 1.1 1 1.2 2.7 1.83 1.18 1.1 ...
## $ Pletelets : num 237000 160000 263358 298000 242000 ...
## $ CPK : int 358 231 582 305 1610 246 582 582 582 898 ...
## Reestruturação do Banco de Dados
db$Event <- ifelse(db$Event == 1, 'Non-Censored', 'Censored')
db$Gender <- ifelse(db$Gender == 1, 'Male', 'Female')
db$Smoking <- ifelse(db$Smoking == 1, 'Yes', 'No')
db$Diabetes <- ifelse(db$Diabetes == 1, 'Yes', 'No')
db$BP <- ifelse(db$BP == 1, 'Yes', 'No')
db$Anaemia <- ifelse(db$Anaemia == 1, 'Yes', 'No')
## Tabelas de Frequência: Variáveis Qualitativas
library(summarytools)
# Variáveis Qualitativas: Colunas 2 à 7
tabs.quali <- list()
for(i in 2:7)
{
tabs.quali[[i - 1]] <- freq(db[,i])
}
tabs.quali## Frequencies
##
## Freq % Valid % Valid Cum. % Total % Total Cum.
## ------------------ ------ --------- -------------- --------- --------------
## Censored 203 67.89 67.89 67.89 67.89
## Non-Censored 96 32.11 100.00 32.11 100.00
## <NA> 0 0.00 100.00
## Total 299 100.00 100.00 100.00 100.00
##
## Type: Character
##
## Freq % Valid % Valid Cum. % Total % Total Cum.
## ------------ ------ --------- -------------- --------- --------------
## Female 105 35.12 35.12 35.12 35.12
## Male 194 64.88 100.00 64.88 100.00
## <NA> 0 0.00 100.00
## Total 299 100.00 100.00 100.00 100.00
##
## Type: Character
##
## Freq % Valid % Valid Cum. % Total % Total Cum.
## ----------- ------ --------- -------------- --------- --------------
## No 203 67.89 67.89 67.89 67.89
## Yes 96 32.11 100.00 32.11 100.00
## <NA> 0 0.00 100.00
## Total 299 100.00 100.00 100.00 100.00
##
## Type: Character
##
## Freq % Valid % Valid Cum. % Total % Total Cum.
## ----------- ------ --------- -------------- --------- --------------
## No 174 58.19 58.19 58.19 58.19
## Yes 125 41.81 100.00 41.81 100.00
## <NA> 0 0.00 100.00
## Total 299 100.00 100.00 100.00 100.00
##
## Type: Character
##
## Freq % Valid % Valid Cum. % Total % Total Cum.
## ----------- ------ --------- -------------- --------- --------------
## No 194 64.88 64.88 64.88 64.88
## Yes 105 35.12 100.00 35.12 100.00
## <NA> 0 0.00 100.00
## Total 299 100.00 100.00 100.00 100.00
##
## Type: Character
##
## Freq % Valid % Valid Cum. % Total % Total Cum.
## ----------- ------ --------- -------------- --------- --------------
## No 170 56.86 56.86 56.86 56.86
## Yes 129 43.14 100.00 43.14 100.00
## <NA> 0 0.00 100.00
## Total 299 100.00 100.00 100.00 100.00
Por outro lado, para o caso das variáveis contínuas, podemos seguir a seguinte rotina:
## Tabelas de Frequência: Variáveis Quantitativas
library(agricolae)
# Variáveis Qualitativas: Colunas 1, 8 à 13
tabs.quanti <- list()
for(i in c(1,8:13))
{
tabs.quanti[[i]] <- table.freq(graph.freq(db[,i], plot = FALSE))
}
tabs.quanti## [[1]]
## Lower Upper Main Frequency Percentage CF CPF
## 1 0 32 16 41 13.7 41 13.7
## 2 32 64 48 24 8.0 65 21.7
## 3 64 96 80 60 20.1 125 41.8
## 4 96 128 112 39 13.0 164 54.8
## 5 128 160 144 19 6.4 183 61.2
## 6 160 192 176 30 10.0 213 71.2
## 7 192 224 208 44 14.7 257 86.0
## 8 224 256 240 31 10.4 288 96.3
## 9 256 288 272 11 3.7 299 100.0
##
## [[2]]
## NULL
##
## [[3]]
## NULL
##
## [[4]]
## NULL
##
## [[5]]
## NULL
##
## [[6]]
## NULL
##
## [[7]]
## NULL
##
## [[8]]
## Lower Upper Main Frequency Percentage CF CPF
## 1 40.0 46.1 43.05 40 13.4 40 13.4
## 2 46.1 52.2 49.15 43 14.4 83 27.8
## 3 52.2 58.3 55.25 42 14.0 125 41.8
## 4 58.3 64.4 61.35 59 19.7 184 61.5
## 5 64.4 70.5 67.45 63 21.1 247 82.6
## 6 70.5 76.6 73.55 22 7.4 269 90.0
## 7 76.6 82.7 79.65 16 5.4 285 95.3
## 8 82.7 88.8 85.75 8 2.7 293 98.0
## 9 88.8 94.9 91.85 4 1.3 297 99.3
## 10 94.9 101.0 97.95 2 0.7 299 100.0
##
## [[9]]
## Lower Upper Main Frequency Percentage CF CPF
## 1 14.0 21.3 17.65 23 7.7 23 7.7
## 2 21.3 28.6 24.95 36 12.0 59 19.7
## 3 28.6 35.9 32.25 83 27.8 142 47.5
## 4 35.9 43.2 39.55 77 25.8 219 73.2
## 5 43.2 50.5 46.85 41 13.7 260 87.0
## 6 50.5 57.8 54.15 3 1.0 263 88.0
## 7 57.8 65.1 61.45 34 11.4 297 99.3
## 8 65.1 72.4 68.75 1 0.3 298 99.7
## 9 72.4 79.7 76.05 0 0.0 298 99.7
## 10 79.7 87.0 83.35 1 0.3 299 100.0
##
## [[10]]
## Lower Upper Main Frequency Percentage CF CPF
## 1 110 114 112 1 0.3 1 0.3
## 2 114 118 116 1 0.3 2 0.7
## 3 118 122 120 1 0.3 3 1.0
## 4 122 126 124 2 0.7 5 1.7
## 5 126 130 128 8 2.7 13 4.3
## 6 130 134 132 38 12.7 51 17.1
## 7 134 138 136 126 42.1 177 59.2
## 8 138 142 140 92 30.8 269 90.0
## 9 142 146 144 28 9.4 297 99.3
## 10 146 150 148 2 0.7 299 100.0
##
## [[11]]
## Lower Upper Main Frequency Percentage CF CPF
## 1 0.50 1.49 0.995 227 75.9 227 75.9
## 2 1.49 2.48 1.985 49 16.4 276 92.3
## 3 2.48 3.47 2.975 11 3.7 287 96.0
## 4 3.47 4.46 3.965 6 2.0 293 98.0
## 5 4.46 5.45 4.955 1 0.3 294 98.3
## 6 5.45 6.44 5.945 2 0.7 296 99.0
## 7 6.44 7.43 6.935 1 0.3 297 99.3
## 8 7.43 8.42 7.925 0 0.0 297 99.3
## 9 8.42 9.41 8.915 2 0.7 299 100.0
##
## [[12]]
## Lower Upper Main Frequency Percentage CF CPF
## 1 20000 112000 66000 9 3.0 9 3.0
## 2 112000 204000 158000 58 19.4 67 22.4
## 3 204000 296000 250000 148 49.5 215 71.9
## 4 296000 388000 342000 57 19.1 272 91.0
## 5 388000 480000 434000 18 6.0 290 97.0
## 6 480000 572000 526000 6 2.0 296 99.0
## 7 572000 664000 618000 1 0.3 297 99.3
## 8 664000 756000 710000 1 0.3 298 99.7
## 9 756000 848000 802000 0 0.0 298 99.7
## 10 848000 940000 894000 1 0.3 299 100.0
##
## [[13]]
## Lower Upper Main Frequency Percentage CF CPF
## 1 20 892 456 255 85.3 255 85.3
## 2 892 1764 1328 20 6.7 275 92.0
## 3 1764 2636 2200 14 4.7 289 96.7
## 4 2636 3508 3072 3 1.0 292 97.7
## 5 3508 4380 3944 2 0.7 294 98.3
## 6 4380 5252 4816 2 0.7 296 99.0
## 7 5252 6124 5688 1 0.3 297 99.3
## 8 6124 6996 6560 0 0.0 297 99.3
## 9 6996 7868 7432 2 0.7 299 100.0
3.3. Representações Gráficas
Uma segunda maneira de resumir e exibir dados é por meio do uso de representações gráficas. Em geral, os gráficos devem ser projetados de modo que transmitam os padrões em um conjunto de observações em um único olhar. No entanto, embora sejam mais fáceis de ler do que tabelas, os gráficos geralmente fornecem um grau menor de detalhes, mas também devem ser simples e autoexplicativos.
3.3.1. Gráfico de Barras
Os gráficos de barras são um tipo popular de gráfico
usado para exibir uma distribuição de frequência para dados nominais,
ordinais ou discretos. Em um gráfico de barras, as categorias nas quais
as observações se enquadram são apresentadas em um eixo horizontal. Em
seguida, uma barra vertical é desenhada acima de cada categoria, de modo
que a altura da barra represente a frequência absoluta ou relativa das
observações dentro dessa categoria. As barras devem ter a mesma largura
e separadas umas das outras para não implicar continuidade. No R, o
gráfico de barras é obtido pela função barplot(). Sendo
assim, considerando a base de dados mtcars disponível no R,
podemos, por exemplo, obter o gráfico de barras da variável
cyl referente ao número de cilindros dos carros registrados
pela rotina:
# Leitura dos dados
data(mtcars)
db.mtcars <- mtcars
# Visualização dos dados
head(db.mtcars, n = 10)## mpg cyl disp hp drat wt qsec vs am gear carb
## Mazda RX4 21.0 6 160.0 110 3.90 2.620 16.46 0 1 4 4
## Mazda RX4 Wag 21.0 6 160.0 110 3.90 2.875 17.02 0 1 4 4
## Datsun 710 22.8 4 108.0 93 3.85 2.320 18.61 1 1 4 1
## Hornet 4 Drive 21.4 6 258.0 110 3.08 3.215 19.44 1 0 3 1
## Hornet Sportabout 18.7 8 360.0 175 3.15 3.440 17.02 0 0 3 2
## Valiant 18.1 6 225.0 105 2.76 3.460 20.22 1 0 3 1
## Duster 360 14.3 8 360.0 245 3.21 3.570 15.84 0 0 3 4
## Merc 240D 24.4 4 146.7 62 3.69 3.190 20.00 1 0 4 2
## Merc 230 22.8 4 140.8 95 3.92 3.150 22.90 1 0 4 2
## Merc 280 19.2 6 167.6 123 3.92 3.440 18.30 1 0 4 4
# Gráfico de barras da variável 'cyl'
# Uso da função barplot()
# barplot(height, width = 1, space = NULL,
# names.arg = NULL, legend.text = NULL, beside = FALSE,
# horiz = FALSE, density = NULL, angle = 45,
# col = NULL, border = par("fg"),
# main = NULL, sub = NULL, xlab = NULL, ylab = NULL,
# xlim = NULL, ylim = NULL, xpd = TRUE, log = "",
# axes = TRUE, axisnames = TRUE,
# cex.axis = par("cex.axis"), cex.names = par("cex.axis"),
# inside = TRUE, plot = TRUE, axis.lty = 0, offset = 0,
# add = FALSE, ann = !add && par("ann"), args.legend = NULL, ...)
cyl <- table(db.mtcars$cyl)
barplot(cyl, main = 'Gráfico de Barras: Nº de Cilindros')Como segundo exemplo, consideremos nossa base de dados
heart com o objetivo de se obter o gráfico de barras da
variável Pletelets referente ao número de plaquetas dos
pacientes com insufiência cardíaca. Observe que a escala do número de
plaquetas é muito alta, então, por questões de melhor visualização,
iremos trabalhar com notação científica e apenas 15 pacientes, neste
caso. Logo, procedemos pela seguinte rotina:
# Leitura dos dados
setwd('datasets')
db <- read.csv('heart.csv', header = TRUE, sep = ',')
# Visualização dos dados
head(db, n = 10)## Time Event Gender Smoking Diabetes BP Anaemia Age Ejection.Fraction Sodium
## 1 97 0 0 0 0 0 1 43 50 135
## 2 180 0 1 1 1 0 1 73 30 142
## 3 31 1 1 1 0 1 0 70 20 134
## 4 87 0 1 0 0 0 1 65 25 141
## 5 113 0 1 0 0 0 0 64 60 137
## 6 10 1 1 0 0 0 1 75 15 137
## 7 250 0 1 1 0 0 0 70 40 136
## 8 27 1 1 0 1 1 0 94 38 134
## 9 87 0 1 0 0 1 0 75 45 137
## 10 87 0 1 1 0 0 0 80 25 144
## Creatinine Pletelets CPK
## 1 1.30 237000 358
## 2 1.18 160000 231
## 3 1.83 263358 582
## 4 1.10 298000 305
## 5 1.00 242000 1610
## 6 1.20 127000 246
## 7 2.70 51000 582
## 8 1.83 263358 582
## 9 1.18 263358 582
## 10 1.10 149000 898
# Gráfico de barras da variável 'Pletelets'
db1 <- db[1:15,]
par(mar = c(5,5,5,1))
barplot(db1$Pletelets,
axes = FALSE,
names.arg = 1:15,
xlab = 'ID do Paciente',
main = 'Gráfico de barras referente ao número de plaquetas \n dos pacientes com insufiência cardiáca',
ylim = c(0, 350000),
ylab = 'Número de Plaquetas (x 10³)',
cex.lab = 1.2)
axis(side = 2, at = seq(0, 350000, 50000), labels = seq(0, 350, 50), las = 1)3.3.2. Gráfico de Setores
Os gráficos de setores apresentam as mesmas
informações que os gráficos de barras, mas na forma de um círculo ou
“pizza”. Neste tipo gráfico, o círculo é dividido em fatias, uma para
cada categoria de dados, e o tamanho de cada fatia é determinado pela
sua medição angular. Isto é, uma vez que um círculo contém 360°, uma
fatia que contém, por exemplo, 50% dos casos teria uma medida angular de
180°. Como regra geral, se a fatia contém \(0\% < \alpha < 100\%\) dos casos, o
ângulo para a fatia será proporcional à \(360º
\cdot \alpha\). No R, o gráfico de setores é obtido pela função
pie(). Sendo assim, considerando a base de dados
mtcars disponível no R, podemos, por exemplo, obter o
gráfico de setores da variável cyl referente ao número de
cilindros dos carros registrados pela rotina:
# Leitura dos dados
data(mtcars)
db.mtcars <- mtcars
# Visualização dos dados
head(db.mtcars, n = 10)## mpg cyl disp hp drat wt qsec vs am gear carb
## Mazda RX4 21.0 6 160.0 110 3.90 2.620 16.46 0 1 4 4
## Mazda RX4 Wag 21.0 6 160.0 110 3.90 2.875 17.02 0 1 4 4
## Datsun 710 22.8 4 108.0 93 3.85 2.320 18.61 1 1 4 1
## Hornet 4 Drive 21.4 6 258.0 110 3.08 3.215 19.44 1 0 3 1
## Hornet Sportabout 18.7 8 360.0 175 3.15 3.440 17.02 0 0 3 2
## Valiant 18.1 6 225.0 105 2.76 3.460 20.22 1 0 3 1
## Duster 360 14.3 8 360.0 245 3.21 3.570 15.84 0 0 3 4
## Merc 240D 24.4 4 146.7 62 3.69 3.190 20.00 1 0 4 2
## Merc 230 22.8 4 140.8 95 3.92 3.150 22.90 1 0 4 2
## Merc 280 19.2 6 167.6 123 3.92 3.440 18.30 1 0 4 4
# Gráfico de setores da variável 'cyl'
# Uso da função pie()
# pie(x, labels = names(x), edges = 200, radius = 0.8,
# clockwise = FALSE, init.angle = if(clockwise) 90 else 0,
# density = NULL, angle = 45, col = NULL, border = NULL,
# lty = NULL, main = NULL, ...)
# Uso da função legend()
# legend(x, y, # Coordinates (x also accepts keywords)
# legend, # Vector with the name of each group
# fill, # Creates boxes in the legend with the specified colors
# col = par("col"), # Color of lines or symbols
# border = "black", # Fill box border color
# lty, lwd, # Line type and width
# pch, # Add pch symbols to legend lines or boxes
# bty = "o", # Box type (bty = "n" removes the box)
# bg = par("bg") # Background color of the legend
# box.lwd = par("lwd"), # Legend box line width
# box.lty = par("lty"), # Legend box line type
# box.col = par("fg"), # Legend box line color
# cex = 1, # Legend size
# horiz = FALSE # Horizontal (TRUE) or vertical (FALSE) legend
# title = NULL # Legend title
# )
# Uso da função round():
# round(x, digits = 0)
cyl <- table(db.mtcars$cyl)
cylpercent <- round(100*cyl/sum(cyl),1)
pie(cyl, main = 'Gráfico de Setores: Nº de Cilindros',
labels = paste(cylpercent, '%'), col = c('darkred', 'gray', 'darkgreen'), border = 'white')
legend('topright', legend = c('4','6','8'),
fill = c('darkred', 'gray', 'darkgreen'),
title = 'Nº de Cilindros',
bty = 'n')Como segundo exemplo, considere as principais causas de morte nos
Estados Unidos no ano de 2009. Neste caso, as causas são obtidas pelo
vetor death e a frequência absoluta das causas é obtida a
partir do comando table. O gráfico de setores, então, pode
ser determinado pela seguinte rotina:
## Base de Dados
death <- c(rep('Doenças do Coração', 25), rep('Câncer', 23), rep('Doenças Respiratórias', 6),
rep('Acidentes', 5), rep('Alzheimer', 3), rep('Diabetes', 3), rep('Pneumonia', 2),
rep('Outras', 34))
## Frequências Absolutas
death <- table(death)
## Frequências Relativas em Porcentagem
death.percent <- round(100*death/sum(death),1)
## Nome das Categorias
death.names <- c('Acidentes', 'Alzheimer', 'Câncer', 'Diabetes', 'Doenças do Coração',
'Doenças Respiratórias', 'Outras', 'Pneumonia')
## Gráfico
pie(death,
main = 'Gráfico de Setores: As principais causas de morte \n nos Estados Unidos em 2009 (Gordis, 2017)',
labels = paste(death.names, death.percent, '%'),
col = rainbow(8),
border = 'white')3.3.3. Gráfico de Linhas
Os gráficos de linhas mostram as informações como
uma série de pontos de dados que estão conectadas por segmentos de linha
reta. Este tipo gráfico tem dois eixos: o eixo X que normalmente
representa os períodos de tempo e o eixo Y tem um valor quantitativo.
Para construir este gráfico, ordena-se os dados em pares em que a
primeira coordenada representa a variável temporal e a segunda
representa a variável quantitativa de interesse ao estudo. Em seguida,
marca-se os pontos relativos os pares de dados, ligando-os com um
segmento de reta. No R, este gráfico é obtido pela função
plot() com argumento type = 'o'. Para
exemplificar, considerando a base de dados Nile disponível
no R, podemos obter o gráfico de linhas referente as medições do fluxo
anual do rio Nilo em Aswan no período de 1871-1970 pela rotina:
# Leitura dos dados
data(Nile)
db.Nile <- Nile
# Visualização dos dados
head(db.Nile, n = 100)## [1] 1120 1160 963 1210 1160 1160 813 1230 1370 1140 995 935 1110 994 1020
## [16] 960 1180 799 958 1140 1100 1210 1150 1250 1260 1220 1030 1100 774 840
## [31] 874 694 940 833 701 916 692 1020 1050 969 831 726 456 824 702
## [46] 1120 1100 832 764 821 768 845 864 862 698 845 744 796 1040 759
## [61] 781 865 845 944 984 897 822 1010 771 676 649 846 812 742 801
## [76] 1040 860 874 848 890 744 749 838 1050 918 986 797 923 975 815
## [91] 1020 906 901 1170 912 746 919 718 714 740
# Gráfico de linhas das medições do fluxo anual do rio Nilo
# Uso da função plot()
# plot(x,y, col = NULL, type = NULL, pch = NULL, lwd = NULL, main = NULL, ...)
plot(db.Nile, type = 'o', lwd = 2, pch = 19, ylab = 'Nile Annual Flow',
main = 'Gráfico de Linhas: Fluxo Anual do Rio Nilo')Como segundo exemplo, considere uma base de dados referentes a
COVID-19, denominada db.covid. A COVID-19 é uma doença
infecciosa causada pelo coronavírus SARS-CoV-2 e tem como principais
sintomas febre, cansaço e tosse seca. Em 26 de novembro de 2021, a OMS
designou a variante da COVID-19 B.1.1.529 como uma variante de
preocupação denominada Ômicron. Essa variante apresenta um grande número
de mutações, algumas das quais preocupantes. As outras variantes de
preocupação ainda estão em circulação e são: Alfa, Beta, Gama e Delta.
Então, para ilustrar nosso gráfico de linhas, consideremos o número de
mortes por COVID-19 nos 15 dias finais do mês de outubro de 2021 no
Brasil.
## Base de Dados
data <- as.Date(c('2021-10-16', '2021-10-17', '2021-10-18', '2021-10-19', '2021-10-20',
'2021-10-21', '2021-10-22', '2021-10-23', '2021-10-24', '2021-10-25',
'2021-10-26', '2021-10-27', '2021-10-28', '2021-10-29', '2021-10-30'))
mortes <- c(465,128,202,379,399,403,447,350,119,207,403,435,373,408,216)
db.covid <- data.frame(data, mortes)
names(db.covid) <- c('Data', 'Mortes')
## Visualização dos Dados
head(db.covid)## Data Mortes
## 1 2021-10-16 465
## 2 2021-10-17 128
## 3 2021-10-18 202
## 4 2021-10-19 379
## 5 2021-10-20 399
## 6 2021-10-21 403
## Gráfico
plot(x = db.covid$Data,
y = db.covid$Mortes,
type = 'o',
lwd = 2,
pch = 19,
ylab = 'Número de Mortes por COVID-19',
xlab = 'Data',
main = 'Gráfico de Linhas: Mortes por COVID-19 em outubro de 2021 no Brasil')3.3.4. Histograma
Talvez o tipo de gráfico mais usado seja o
histograma. Enquanto um gráfico de barras é uma
representação de uma distribuição de frequência para dados nominais ou
ordinais, um histograma descreve uma distribuição de frequência para
dados agrupados em classes. Para a construção do histograma, no eixo
horizontal, especifique os intervalos de classe. No outro eixo,
especifique escala de frequência. Em seguida, usando uma barra de
largura fixa desenhada acima de cada intervalo de classe, estenda a
barra até atingir a frequência da classe. No R, este gráfico é obtido
pela função hist(). Para exemplificar, considerando a base
de dados heart, podemos obter o histograma referente aos
níveis de sódio fornecidos pela variável Sodium pela
rotina:
# Definir o diretório onde se encontram os dados
setwd('datasets')
# Leitura dos dados pela função read.csv():
# read.csv(file, header = TRUE, sep = ",", quote = "\"",
# dec = ".", fill = TRUE, comment.char = "", ...)
db.heart <- read.csv('heart.csv', header = TRUE, sep = ',')
# Visualização dos dados
head(db.heart, n = 10)## Time Event Gender Smoking Diabetes BP Anaemia Age Ejection.Fraction Sodium
## 1 97 0 0 0 0 0 1 43 50 135
## 2 180 0 1 1 1 0 1 73 30 142
## 3 31 1 1 1 0 1 0 70 20 134
## 4 87 0 1 0 0 0 1 65 25 141
## 5 113 0 1 0 0 0 0 64 60 137
## 6 10 1 1 0 0 0 1 75 15 137
## 7 250 0 1 1 0 0 0 70 40 136
## 8 27 1 1 0 1 1 0 94 38 134
## 9 87 0 1 0 0 1 0 75 45 137
## 10 87 0 1 1 0 0 0 80 25 144
## Creatinine Pletelets CPK
## 1 1.30 237000 358
## 2 1.18 160000 231
## 3 1.83 263358 582
## 4 1.10 298000 305
## 5 1.00 242000 1610
## 6 1.20 127000 246
## 7 2.70 51000 582
## 8 1.83 263358 582
## 9 1.18 263358 582
## 10 1.10 149000 898
# Histograma dos níveis de sódio
# Uso da função hist()
# hist(x, breaks = "Sturges",
# freq = NULL, probability = !freq,
# include.lowest = TRUE, right = TRUE,
# density = NULL, angle = 45, col = NULL, border = NULL,
# main = paste("Histogram of" , xname),
# xlim = range(breaks), ylim = NULL,
# xlab = xname, ylab,
# axes = TRUE, plot = TRUE, labels = FALSE,
# nclass = NULL, warn.unused = TRUE, ...)
hist(db.heart$Sodium, main = 'Histograma: Níveis de Sódio', xlab = 'Níveis de Sódio')3.3.5. Gráfico de Densidade
Um outro tipo gráfico importante que temos é o
gráfico de densidade que é uma versão suavizada do
histograma e é usado no mesmo conceito, ou seja, para
representar a distribuição de uma variável numérica. No R, este gráfico
é obtido pela combinação das funções funções plot() e
density(). Para exemplificar, considerando a base de dados
heart, podemos obter o gráfico de densidade referente aos
níveis de sódio fornecidos pela variável Sodium pela
rotina:
# Definir o diretório onde se encontram os dados
setwd('datasets')
# Leitura dos dados pela função read.csv():
# read.csv(file, header = TRUE, sep = ",", quote = "\"",
# dec = ".", fill = TRUE, comment.char = "", ...)
db.heart <- read.csv('heart.csv', header = TRUE, sep = ',')
# Visualização dos dados
head(db.heart, n = 10)## Time Event Gender Smoking Diabetes BP Anaemia Age Ejection.Fraction Sodium
## 1 97 0 0 0 0 0 1 43 50 135
## 2 180 0 1 1 1 0 1 73 30 142
## 3 31 1 1 1 0 1 0 70 20 134
## 4 87 0 1 0 0 0 1 65 25 141
## 5 113 0 1 0 0 0 0 64 60 137
## 6 10 1 1 0 0 0 1 75 15 137
## 7 250 0 1 1 0 0 0 70 40 136
## 8 27 1 1 0 1 1 0 94 38 134
## 9 87 0 1 0 0 1 0 75 45 137
## 10 87 0 1 1 0 0 0 80 25 144
## Creatinine Pletelets CPK
## 1 1.30 237000 358
## 2 1.18 160000 231
## 3 1.83 263358 582
## 4 1.10 298000 305
## 5 1.00 242000 1610
## 6 1.20 127000 246
## 7 2.70 51000 582
## 8 1.83 263358 582
## 9 1.18 263358 582
## 10 1.10 149000 898
# Gráfico de densidade
# Uso da função density()
# density(x, bw = "nrd0", adjust = 1,
# kernel = c("gaussian", "epanechnikov", "rectangular",
# "triangular", "biweight", "cosine", "optcosine"),
# weights = NULL, window = kernel, width,
# give.Rkern = FALSE, subdensity = FALSE,
# n = 512, from, to, cut = 3, na.rm = FALSE, ...)
plot(density(db.heart$Sodium), main = 'Gráfico de Densidade: Níveis de Sódio', xlab = 'Níveis de Sódio')3.3.6. Boxplot
Um outro tipo de representação gráfica para o resumo dos dados brutos
é o gráfico de Box-Whisker (ou
boxplot), que traz uma perspectiva sobre o
comportamento e variabilidade dos dados. Este gráfico é uma alternativa
ao histograma, sendo utilizado quando queremos representar a
distribuição de uma variável quantitativa ou, pelo menos, qualitativa
ordinal. Além da variável numérica/ordinal, pode-se incluir uma variável
de grupo. No R, este gráfico é obtido pela função
boxplot(). Para exemplificar, considerando a base de dados
heart, podemos obter o boxplot referente ao níveis de sódio
(variável Sodium) de acordo com o status de diabetes
(variável Diabetes) pela rotina:
# Definir o diretório onde se encontram os dados
setwd('datasets')
# Leitura dos dados pela função read.csv():
# read.csv(file, header = TRUE, sep = ",", quote = "\"",
# dec = ".", fill = TRUE, comment.char = "", ...)
db.heart <- read.csv('heart.csv', header = TRUE, sep = ',')
# Visualização dos dados
head(db.heart, n = 10)## Time Event Gender Smoking Diabetes BP Anaemia Age Ejection.Fraction Sodium
## 1 97 0 0 0 0 0 1 43 50 135
## 2 180 0 1 1 1 0 1 73 30 142
## 3 31 1 1 1 0 1 0 70 20 134
## 4 87 0 1 0 0 0 1 65 25 141
## 5 113 0 1 0 0 0 0 64 60 137
## 6 10 1 1 0 0 0 1 75 15 137
## 7 250 0 1 1 0 0 0 70 40 136
## 8 27 1 1 0 1 1 0 94 38 134
## 9 87 0 1 0 0 1 0 75 45 137
## 10 87 0 1 1 0 0 0 80 25 144
## Creatinine Pletelets CPK
## 1 1.30 237000 358
## 2 1.18 160000 231
## 3 1.83 263358 582
## 4 1.10 298000 305
## 5 1.00 242000 1610
## 6 1.20 127000 246
## 7 2.70 51000 582
## 8 1.83 263358 582
## 9 1.18 263358 582
## 10 1.10 149000 898
# Boxplot dos níveis de sódio de acordo com o status de diabetes
# Uso da função boxplot()
# boxplot(formula, data = NULL, .., subset, na.action = NULL,
# xlab = mklab(y_var = horizontal),
# ylab = mklab(y_var =!horizontal),
# add = FALSE, ann = !add, horizontal = FALSE,
# drop = FALSE, sep = ".", lex.order = FALSE)
# Ou
# boxplot(x, ..., range = 1.5, width = NULL, varwidth = FALSE,
# notch = FALSE, outline = TRUE, names, plot = TRUE,
# border = par("fg"), col = NULL, log = "",
# pars = list(boxwex = 0.8, staplewex = 0.5, outwex = 0.5),
# ann = !add, horizontal = FALSE, add = FALSE, at = NULL)
boxplot(db.heart$Sodium~db.heart$Diabetes, main = 'Boxplot: Níveis de Sódio',
names = c('Sim', 'Não'), ylab = 'Níveis de Sódio', xlab = 'Diabetes', pch = 19)3.3.7. Diagrama de Dispersão
Um outro tipo de representação gráfica para o resumo dos dados brutos
é o diagrama de dispersão que é um gráfico onde pontos
no espaço cartesiano XY são usados para representar de maneira
simultânea os valores das observações de duas variáveis quantitativas.
No R, este gráfico é obtido pela função plot(). Para
exemplificar, considerando a base de dados heart, podemos
obter o diagrama de dispersão referente ao níveis de creatinina
(variável Creatinine) de acordo com os valores de CPK
(variável CPK) pela rotina:
# Definir o diretório onde se encontram os dados
setwd('datasets')
# Leitura dos dados pela função read.csv():
# read.csv(file, header = TRUE, sep = ",", quote = "\"",
# dec = ".", fill = TRUE, comment.char = "", ...)
db.heart <- read.csv('heart.csv', header = TRUE, sep = ',')
# Visualização dos dados
head(db.heart, n = 10)## Time Event Gender Smoking Diabetes BP Anaemia Age Ejection.Fraction Sodium
## 1 97 0 0 0 0 0 1 43 50 135
## 2 180 0 1 1 1 0 1 73 30 142
## 3 31 1 1 1 0 1 0 70 20 134
## 4 87 0 1 0 0 0 1 65 25 141
## 5 113 0 1 0 0 0 0 64 60 137
## 6 10 1 1 0 0 0 1 75 15 137
## 7 250 0 1 1 0 0 0 70 40 136
## 8 27 1 1 0 1 1 0 94 38 134
## 9 87 0 1 0 0 1 0 75 45 137
## 10 87 0 1 1 0 0 0 80 25 144
## Creatinine Pletelets CPK
## 1 1.30 237000 358
## 2 1.18 160000 231
## 3 1.83 263358 582
## 4 1.10 298000 305
## 5 1.00 242000 1610
## 6 1.20 127000 246
## 7 2.70 51000 582
## 8 1.83 263358 582
## 9 1.18 263358 582
## 10 1.10 149000 898
# Diagrama de dispersão dos níveis de creatinina de acordo com o CPK
# Uso da função plot()
# plot(x,y, col = NULL, type = NULL, pch = NULL, lwd = NULL, main = NULL, ...)
plot(db.heart$Creatinine~db.heart$CPK, main = 'Diagrama de Dispersão: Níveis de Creatinina vs. CPK',
xlab = 'CPK', ylab = 'Níveis de Creatinina', pch = 19)
Como segundo exemplo, consideremos a base de dados
iris com
o objetivo de se obter o gráfico de dispersão das variáveis
Petal.Length referente ao tamanho da pétala da planta, e
Sepal.Length referente ao tamanho da sépala. Diferente do
primeiro exemplo, neste pode-se verificar uma relação mais linear entre
ambas as variáveis.
# Carregar a base de dados
data(iris)
# Visualização dos dados
head(iris, n = 10)## Sepal.Length Sepal.Width Petal.Length Petal.Width Species
## 1 5.1 3.5 1.4 0.2 setosa
## 2 4.9 3.0 1.4 0.2 setosa
## 3 4.7 3.2 1.3 0.2 setosa
## 4 4.6 3.1 1.5 0.2 setosa
## 5 5.0 3.6 1.4 0.2 setosa
## 6 5.4 3.9 1.7 0.4 setosa
## 7 4.6 3.4 1.4 0.3 setosa
## 8 5.0 3.4 1.5 0.2 setosa
## 9 4.4 2.9 1.4 0.2 setosa
## 10 4.9 3.1 1.5 0.1 setosa
# Diagrama de dispersão do tamanho da pétala vs. tamanho da sépala
# Uso da função plot()
# plot(x,y, col = NULL, type = NULL, pch = NULL, lwd = NULL, main = NULL, ...)
plot(iris$Petal.Length~iris$Sepal.Length, main = 'Diagrama de Dispersão: Tamanho da Pétala vs. Tamanho da Sépala',
xlab = 'Tamanho da Sépala', ylab = 'Tamanho da Pétala', pch = 19)3.5.8. Gráficos Interativos
Outro tipo de ferramenta gráfica é trabalhar com gráficos
interativos. Um gráfico interativo é uma forma de apresentar
dados aos usuários que visitam uma página com animações e customizações,
criando uma experiência única para quem deseja conferir certas
informações. Portanto, em vez de apenas apresentar um quadro fixo, é
possível deixar que cada usuário personalize e interaja como desejar com
a imagem. No R, pode-se trabalhar com os pacotes dygraphs,
ggplot2, dplyr, hrbrthemes,
htmlwidgets e xts para a construção desse tipo
gráfico.
3.5.7.1. Pacote: dygraphs
O pacote dygraphs é uma interface R para a biblioteca de
gráficos JavaScript ‘dygraphs’ que fornece recursos avançados para
gráficos de dados de séries temporais em R, incluindo exibição altamente
configurável de séries e eixos e recursos interativos como zoom/pan e
realce de séries/pontos.
# Instalar e carregar os pacotes necessários
# install.packages('dygraphs')
# install.packages('xts')
# install.packages('htmlwidgets')
library(dygraphs)## Warning: package 'dygraphs' was built under R version 4.2.2
library(xts)## Warning: package 'xts' was built under R version 4.2.2
## Loading required package: zoo
##
## Attaching package: 'zoo'
## The following objects are masked from 'package:base':
##
## as.Date, as.Date.numeric
library(htmlwidgets)
# Criar uma base de dados
data <- data.frame(time = seq(from = Sys.Date() - 40, to = Sys.Date(), by = 1), value = runif(41))
# Verificar a estrutura dos dados
str(data)## 'data.frame': 41 obs. of 2 variables:
## $ time : Date, format: "2022-11-22" "2022-11-23" ...
## $ value: num 0.635 0.652 0.867 0.485 0.278 ...
# Converter para formato XTS
# Uso da função xts():
# xts(x, order.by = index(x), frequency = NULL, ...)
data <- xts(x = data$value, order.by = data$time)6.5.7.1.1. Gráfico de ‘Linha com Pontos’
# Uso da função dygraph():
# dygraph(data, main = NULL, xlab = NULL, ylab = NULL, periodicity = NULL,
# group = NULL, elementId = NULL, width = NULL, height = NULL)
p <- dygraph(data) %>%
dyOptions(drawPoints = TRUE, pointSize = 4)
p# Para salvar o gráfico interativo
# saveWidget(p, file=paste0( getwd(), "/HtmlWidget/dygraphs317-1.html"))6.5.7.1.2. Gráfico de ‘Área’
# Uso da função dygraph():
# dygraph(data, main = NULL, xlab = NULL, ylab = NULL, periodicity = NULL,
# group = NULL, elementId = NULL, width = NULL, height = NULL)
p <- dygraph(data) %>%
dyOptions(fillGraph=TRUE)
p6.5.7.1.3. Gráfico de ‘Step’
# Uso da função dygraph():
# dygraph(data, main = NULL, xlab = NULL, ylab = NULL, periodicity = NULL,
# group = NULL, elementId = NULL, width = NULL, height = NULL)
p <- dygraph(data) %>%
dyOptions(stepPlot=TRUE, fillGraph=TRUE)
p6.5.7.1.3. Gráfico de ‘Lollipop’
# Uso da função dygraph():
# dygraph(data, main = NULL, xlab = NULL, ylab = NULL, periodicity = NULL,
# group = NULL, elementId = NULL, width = NULL, height = NULL)
p <- dygraph(data) %>%
dyOptions( stemPlot=TRUE)
p6.5.7.1.4. Gráfico de ‘Candlestick’
# Criar uma base de dados
trend <- sin(seq(1,41))+runif(41)
data <- data.frame(
time=seq(from=Sys.Date()-40, to=Sys.Date(), by=1 ),
value1=trend,
value2=trend+rnorm(41),
value3=trend+rnorm(41),
value4=trend+rnorm(41)
)
# Converter para formato XTS
data <- xts(x = data[,-1], order.by = data$time)
# Grafíco
# Uso da função dygraph():
# dygraph(data, main = NULL, xlab = NULL, ylab = NULL, periodicity = NULL,
# group = NULL, elementId = NULL, width = NULL, height = NULL)
p <- dygraph(data) %>%
dyCandlestick()
p# Salvar o gráfico
# saveWidget(p, file=paste0( getwd(), "/HtmlWidget/dygraphs317-5.html"))6.5.7.1.4. Gráfico de ‘Linhas com Intervalo’
# Criar uma base de dados
trend <- sin(seq(1,41))+runif(41)
data <- data.frame(
time=seq(from=Sys.Date()-40, to=Sys.Date(), by=1 ),
trend=trend,
max=trend+abs(rnorm(41)),
min=trend-abs(rnorm(41, sd=1))
)
# Converter para formato XTS
data <- xts(x = data[,-1], order.by = data$time)
# Gráfico
# Uso da função dygraph():
# dygraph(data, main = NULL, xlab = NULL, ylab = NULL, periodicity = NULL,
# group = NULL, elementId = NULL, width = NULL, height = NULL)
p <- dygraph(data) %>%
dySeries(c("min", "trend", "max"))
p# Salvar o gráfico
# saveWidget(p, file=paste0( getwd(), "/HtmlWidget/dygraphs317-6.html"))3.5.7.2. Pacote: ggplot2
O pacote ggplot2, os gráficos são construídos camada por
camada, sendo a primeira delas dada pela função ggplot()
(repare que não tem o “2”). Essa função recebe um data frame e cria a
camada base do gráfico, o nosso “canvas”, onde acrescentaremos
todos os outros elementos (camadas).
# Instalar e carregar os pacotes necessários
# install.packages('ggplot2')
# install.packages('dplyr')
# install.packages('plotly')
# install.packages('hrbrthemes')
library(ggplot2)## Warning: package 'ggplot2' was built under R version 4.2.2
##
## Attaching package: 'ggplot2'
## The following object is masked from 'package:epiDisplay':
##
## alpha
library(dplyr)## Warning: package 'dplyr' was built under R version 4.2.2
##
## Attaching package: 'dplyr'
## The following objects are masked from 'package:xts':
##
## first, last
## The following object is masked from 'package:MASS':
##
## select
## The following objects are masked from 'package:stats':
##
## filter, lag
## The following objects are masked from 'package:base':
##
## intersect, setdiff, setequal, union
library(plotly)## Warning: package 'plotly' was built under R version 4.2.2
##
## Attaching package: 'plotly'
## The following object is masked from 'package:ggplot2':
##
## last_plot
## The following object is masked from 'package:MASS':
##
## select
## The following object is masked from 'package:stats':
##
## filter
## The following object is masked from 'package:graphics':
##
## layout
library(hrbrthemes)## Warning: package 'hrbrthemes' was built under R version 4.2.2
## NOTE: Either Arial Narrow or Roboto Condensed fonts are required to use these themes.
## Please use hrbrthemes::import_roboto_condensed() to install Roboto Condensed and
## if Arial Narrow is not on your system, please see https://bit.ly/arialnarrow
# Criar uma base de dados (disponível no GitHub)
data <- read.table("https://raw.githubusercontent.com/holtzy/data_to_viz/master/Example_dataset/3_TwoNumOrdered.csv",
header = T)
data$date <- as.Date(data$date)
# Verificar a estrutura dos dados
str(data)## 'data.frame': 1822 obs. of 2 variables:
## $ date : Date, format: "2013-04-28" "2013-04-29" ...
## $ value: num 136 147 147 140 126 ...
6.5.7.2.1. Gráfico de ‘Área’
# Uso da função ggplot():
# ggplot(data = NULL, mapping = aes(), ..., environment = parent.frame())
p <- data %>%
ggplot( aes(x=date, y=value)) +
geom_area(fill="#69b3a2", alpha=0.5) +
geom_line(color="#69b3a2") +
ylab("bitcoin price ($)") +
theme_ipsum()
# Uso da função ggplotly():
# ggplotly(p = ggplot2::last_plot(), width = NULL,
# height = NULL, tooltip = "all", dynamicTicks = FALSE,
# layerData = 1, originalData = TRUE, source = "A", ...)
p <- ggplotly(p)
p# Salvar o gráfico
# saveWidget(p, file=paste0( getwd(), "/HtmlWidget/ggplotlyAreachart.html"))6.5.7.2.2. Gráfico ‘Bubble’
# Criar a base de dados (disponível no GitHub, pelo site do plotly)
data <- read.csv("https://raw.githubusercontent.com/plotly/datasets/master/gapminderDataFiveYear.csv")
# Organizar os dados
data_2007 <- data[which(data$year == 2007),]
data_2007 <- data_2007[order(data_2007$continent, data_2007$country),]
slope <- 0.00005
data_2007$size <- sqrt(data_2007$pop * slope)
colors <- c('#4AC6B7', '#1972A4', '#965F8A', '#FF7070', '#C61951')
# Fazer o gráfico
# Uso da função plot_ly():
# plot_ly(data = data.frame(), ..., type = NULL, name,
# color, colors = NULL, alpha = NULL, stroke, strokes = NULL,
# alpha_stroke = 1, size, sizes = c(10, 100), span, spans = c(1, 20),
# symbol, symbols = NULL, linetype, linetypes = NULL, split,
# frame, width = NULL, height = NULL, source = "A")
fig <- plot_ly(data_2007, x = ~gdpPercap, y = ~lifeExp, color = ~continent, size = ~size, colors = colors,
type = 'scatter', mode = 'markers', sizes = c(min(data_2007$size), max(data_2007$size)),
marker = list(symbol = 'circle', sizemode = 'diameter',
line = list(width = 2, color = '#FFFFFF')),
text = ~paste('Country:', country, '<br>Life Expectancy:', lifeExp, '<br>GDP:', gdpPercap,
'<br>Pop.:', pop))
fig <- fig %>% layout(title = 'Life Expectancy vs. Per Capita GDP, 2007',
xaxis = list(title = 'GDP per capita (2000 dollars)',
gridcolor = 'rgb(255, 255, 255)',
range = c(2.00, 5.20),
type = 'log',
zerolinewidth = 1,
ticklen = 5,
gridwidth = 2),
yaxis = list(title = 'Life Expectancy (Years)',
gridcolor = 'rgb(255, 255, 255)',
range = c(36, 92),
zerolinewidth = 1,
ticklen = 5,
gridwith = 2),
paper_bgcolor = 'rgb(243, 243, 243)',
plot_bgcolor = 'rgb(243, 243, 243)')
fig## Warning: `line.width` does not currently support multiple values.
## Warning: `line.width` does not currently support multiple values.
## Warning: `line.width` does not currently support multiple values.
## Warning: `line.width` does not currently support multiple values.
## Warning: `line.width` does not currently support multiple values.
3.4. Medidas Descritivas
Embora tabelas de frequência e gráficos nos forneçam resumos descritivos úteis, eles não são completos. Em certas situações, por exemplo, temos por interesse resumir os dados brutos por meio de certos valores numéricos que, na Estatística, são denominados medidas descritivas. Essas medidas são classificadas essencialmente em três grupos:
- Posição: ponto em torno do qual se concentram os dados.
- Dispersão: aponta o grau de variabilidade dos dados.
- Associação: síntese da relação entre duas variáveis.
3.4.1. Medidas de Posição
As medidas de posição representam, por definição estatística, a tendência das observações de um conjunto de dados de se agruparem, ou centralizarem, em torno de seu ponto central ou do ponto no qual tais observações estão condensadas.Isto é, estas medidas tem por objetivo representar os dados de uma forma mais condensada que uma tabela de frequências, localizando a maior concentração de valores em torno de uma distribuição de frequências. Dentre tais medidas, destacam-se a média, a moda, e a mediana.
3.4.1.1. Média
3.4.1.1.1. Dados Brutos
Dentre as medidas de posição, a média (ou média aritmética) é a mais popular, sendo calculada pela soma de todos os valores observados da variável dividida pelo número total de observações. Matematicamente, a média é descrita por:
\[\bar{x} = \sum_{i=1}^{n}\dfrac{x_i}{n}\]
No R, a média pode ser facilmente calculada pela função
mean(). Sendo assim, considerando a base de dados
iris disponível no R, podemos obter a média da variável
Sepal.Length é obtida pela rotina:
# Uso da função:
# mean(x, na.rm = FALSE, ...)
mean(iris$Sepal.Length)## [1] 5.843333
Observação: Se houver pelo menos um valor ausente no
conjunto de dados, utilize
mean(iris$Sepal.Length, na.rm = TRUE) para calcular a média
com o NA excluído. Este argumento pode ser usado para a maioria das
funções apresentadas nesta classe, não apenas para a média.
3.4.1.1.2. Dados Agrupados
Porém, quando nossos dados são agrupados em classes, o cálculo da média é um pouco diferente do habitual. Então, para o cálculo da média, neste caso, devemos, em primeiro lugar, determinar o ponto médio da classe que é definido pela equação:
\[\bar{x}_{c_i} = \dfrac{L_i + l_i}{2}\]
onde \(c_i\) representa a respectiva classe, e \(l_i\) e \(L_i\) são os limites da classe. Em seguida, multiplica-se cada valor do ponto médio da classe pela frequência de cada classe, e soma-se cada resultado. Por fim, dividimos o valor obtido pela soma das frequências de cada classe. Em termos matemáticos, tem-se que a média é dada por:
\[ \bar{x} = \dfrac{\sum_{i=1}^{n} f_{a_i} \bar{x}_{c_i}}{\sum_{i=1}^{n} f_{a_i}}\]
onde \(f_{a_i}\) representa a frequência absoluta da classe.
No R, neste caso, não há uma função pronta para o cálculo da média
para dados agrupados, sendo necessário implentar a mesma. Sendo assim,
considerando a base de dados iris disponível no R, podemos
obter a média da variável Sepal.Length definida em uma
tabela de frequências em intervalos de classe pela rotina:
# Carregar Pacote
library(agricolae)
# Cálculo da Média Agrupada
media_agrupada <- function(x)
{
# Uso da função graph.freq()
# graph.freq(x, breaks=NULL,counts=NULL,frequency=1, plot=TRUE, nclass=NULL,
# xlab="",ylab="",axes = "",las=1,...)
# Uso da função table.freq()
# table.freq(object)
tab <- table.freq(graph.freq(x, plot = FALSE))
media <- sum(tab$Main * tab$Frequency)/sum(tab$Frequency)
return(media)
}
media_agrupada(iris$Sepal.Length)## [1] 5.831467
3.4.1.2. Mediana
3.4.1.2.1. Dados Brutos
A mediana é uma medida de posição descrita pelo valor que ocupa a posição central da série de observações de uma variável, dividindo o conjunto em duas partes iguais. Para seu cálculo, devemos seguir os seguintes passos:
Ordenar as observações em ordem crescente ou decrescente.
Determinar o número de observações da base de dados. Se tal número for ímpar, a mediana será a posição central. Mas se tal número for par, a mediana será dada pela média das duas posições centrais.
No R, a mediana pode ser facilmente calculada pela função
median(). Sendo assim, considerando a base de dados
iris disponível no R, podemos obter a mediana da variável
Sepal.Length é obtida pela rotina:
# Uso da função:
# median(x, na.rm = FALSE, ...)
median(iris$Sepal.Length)## [1] 5.8
Uma outra forma de calcular a mediana no R é fazer o uso da função
quantile() com argumento probs = 0.5, pois a
mediana corresponde ao quartil de 50% que veremos mais adiante. Sendo
assim, considerando a base de dados iris disponível no R,
podemos obter a mediana da variável Sepal.Length é obtida
pela rotina:
# Uso da função:
# quantile(x, probs = seq(0, 1, 0.25), na.rm = FALSE,
# names = TRUE, type = 7, digits = 7, ...)
quantile(iris$Sepal.Length, 0.5)## 50%
## 5.8
3.4.1.2.2. Dados Agrupados
Por outro lado, se os dados são agrupados em classes, a mediana é calculada seguindo as etapas: (1) identificar a classe que apresenta a posição central dos dados; (2) calcular a mediana pela equação:
\[\tilde{x} = l_i + \dfrac{h(k − F_{a_{i−1}})}{F_{a_i}} \]
em que \(k = \dfrac{n}{2}\) indica a posição central, \(i\) representa a classe, \(l_i\) é o limite inferior, \(h\) é amplitude, e \(F_{a_i} \geq k\) é a frequência acumulada da classe.
Assim como a média, no R, não há uma função pronta para o cálculo da
mediana para dados agrupados, sendo necessário implentar a mesma. Sendo
assim, considerando a base de dados iris disponível no R,
podemos obter a mediana da variável Sepal.Length definida
em uma tabela de frequências em intervalos de classe pela rotina:
# Carregar Pacote
library(agricolae)
# Cálculo da Mediana Agrupada
mediana_agrupada <- function(x)
{
# Uso da função graph.freq()
# graph.freq(x, breaks=NULL,counts=NULL,frequency=1, plot=TRUE, nclass=NULL,
# xlab="",ylab="",axes = "",las=1,...)
# Uso da função table.freq()
# table.freq(object)
tab <- table.freq(graph.freq(x, plot = FALSE))
# Uso da função which()
# which(x, arr.ind = FALSE, useNames = TRUE)
h <- min(tab$Upper) - min(tab$Lower) # Amplitude
k <- sum(tab$Frequency)/2 # Posição Central
li <- tab$Lower[min(which(tab$CPF >= 50.0))] # Limite Inferior (Classe da Mediana)
Fa_1 <- tab$CF[min(which(tab$CPF >= 50.0)) - 1] # Frequência Acumulada (Anterior a Classe da Mediana)
Fa <- tab$CF[min(which(tab$CPF >= 50.0))] # Frequêncua Acumulada (Classe da Mediana)
mediana <- li + (h * (k - Fa_1))/Fa
return(mediana)
}
mediana_agrupada(iris$Sepal.Length)## [1] 5.662697
3.4.1.3. Moda
3.4.1.3.1. Dados Brutos
A moda é caracterizada pelo valor que apresenta a
maior frequência da variável entre os valores observados. No entanto, no
R, não há uma função para encontrar a moda de um conjunto de dados. Para
resolver essa questão, podemos trabalhar com duas funções de suma
importância, as funções table() e sort().
Sendo assim, considerando a base de dados iris disponível
no R, podemos obter a moda da variável Sepal.Length é
obtida pela rotina:
# Uso da função table()
# table(..., exclude = if (useNA == "no") c(NA, NaN),
# useNA = c("no", "ifany", "always"), dnn = list.names(...),
# deparse.level = 1)
tab <- table(iris$Sepal.Length) # Frequência das observações
# Uso da função sort()
# sort(x, decreasing = FALSE, ...)
sort(tab, decreasing = TRUE) # Ordena do maior para o menor##
## 5 5.1 6.3 5.7 6.7 5.5 5.8 6.4 4.9 5.4 5.6 6 6.1 4.8 6.5 4.6 5.2 6.2 6.9 7.7
## 10 9 9 8 8 7 7 7 6 6 6 6 6 5 5 4 4 4 4 4
## 4.4 5.9 6.8 7.2 4.7 6.6 4.3 4.5 5.3 7 7.1 7.3 7.4 7.6 7.9
## 3 3 3 3 2 2 1 1 1 1 1 1 1 1 1
A função table() fornece o número de ocorrências para
cada valor exclusivo e, em seguida, a função sort() com o
argumento decreasing = TRUE exibe o número de ocorrências
do maior para o menor. A moda, então, da variável
Sepal.Length é igual a \(x_m =
5\).
3.4.1.3.2. Dados Agrupados
Se os dados são agrupados em classes, a moda é calculada seguindo as etapas: (1) identificar a classe que apresenta a maior frequência; (2) calcular a moda pela equação:
\[x_m = l_i + \dfrac{h(f_{a_i} - f_{a_{i-i}})}{(f_{a_i} - f_{a_{i-i}}) + (f_{a_i} - f_{a_{i+i}})}\]
em que \(i\) representa a classe, \(l_i\) é o limite inferior da classe, \(h\) é amplitude da classe, e \(f_{a_i}\) é a frequência absoluta da classe.
Assim como a média e a mediana, no R, não há uma função pronta para o
cálculo da moda para dados agrupados, sendo necessário implentar a
mesma. Sendo assim, considerando a base de dados iris
disponível no R, podemos obter a moda da variável
Sepal.Length definida em uma tabela de frequências em
intervalos de classe pela rotina:
# Carregar Pacote
library(agricolae)
# Cálculo da Mediana Agrupada
moda_agrupada <- function(x)
{
# Uso da função graph.freq()
# graph.freq(x, breaks=NULL,counts=NULL,frequency=1, plot=TRUE, nclass=NULL,
# xlab="",ylab="",axes = "",las=1,...)
# Uso da função table.freq()
# table.freq(object)
tab <- table.freq(graph.freq(x, plot = FALSE))
# Uso da função which()
# which.max(x)
h <- min(tab$Upper) - min(tab$Lower) # Amplitude
li <- tab$Lower[which.max(tab$Frequency)] # Limite Inferior (Classe Modal)
fa_1 <- tab$Frequency[which.max(tab$Frequency) - 1] # Frequência Absoluta (Anterior a Classe Modal)
fa_2 <- tab$Frequency[which.max(tab$Frequency) + 1] # Frequência Absoluta (Posterior a Classe Modal)
fa <- tab$Frequency[which.max(tab$Frequency)] # Frequêncua Absoluta (Classe Modal)
moda <- li + (h * (fa - fa_1))/((fa - fa_1) + (fa - fa_2))
return(moda)
}
moda_agrupada(iris$Sepal.Length)## [1] 4.945946
3.4.2. Medidas de Dispersão
3.4.2.1. Amplitude
A amplitude de um conjunto, em Estatística, é a
diferença entre o maior elemento desse conjunto e o menor. Em outras
palavras, para encontrar a amplitude de uma lista de números, basta
subtrair o menor elemento do maior. No R, há duas formas de calcularmos
a aplitude, a primeira é trabalhando com as funções min() e
max(), e a segunda é trabalhando com a função
range(). Para o primeiro caso, considerando a base de dados
iris disponível no R, a amplitude da variável
Sepal.Length é obtida pela rotina:
# Uso da função min():
# min(..., na.rm = FALSE)
min(iris$Sepal.Length)## [1] 4.3
# Uso da função max():
# max(..., na.rm = FALSE)
max(iris$Sepal.Length)## [1] 7.9
# Amplitude
A1 <- max(iris$Sepal.Length) - min(iris$Sepal.Length)
A1## [1] 3.6
3.4.2.2. Amplitude Interquartil
O primeiro conceito que devemos definir nessa seção é, primeiramente,
o conceito de quartis. Os quartis, tem
por objetivo dividir uma série de observações de uma variável de um
conjunto de dados em quatro partes iguais, e são denominados \(Q_1\) (1º quartil, 25%), \(Q_2\) (2º quartil ou mediana, 50%), e \(Q_3\) (3º quartil, 75%). Para calcular tais
quartis, seguimos o mesmo procedimento de cálculo da mediana. No
entanto, há um critério de ordem para os cálculos. Isto é, primeiro,
calcula-se \(Q_2\) (ou mediana). Em
seguida, \(Q_1\) usando o mesmo método
aplicado para \(Q_2\), e, por fim,
calcula-se \(Q_3\) pelo mesmo método.
No R, para trabalhar com esse conceito, se faz o uso da função
quantile() com argumento probs = 0.25 para o
1º quartil, probs = 0.50 para o 2º quartil, e, por fim,
probs = 0.75 para o 3º quartil. Sendo assim, considerando a
base de dados iris disponível no R, podemos obter a mediana
da variável Sepal.Length é obtida pela rotina:
# Uso da função:
# quantile(x, probs = seq(0, 1, 0.25), na.rm = FALSE,
# names = TRUE, type = 7, digits = 7, ...)
quantile(iris$Sepal.Length, 0.25) # Primeiro Quartil## 25%
## 5.1
quantile(iris$Sepal.Length, 0.50) # Segundo Quartil## 50%
## 5.8
quantile(iris$Sepal.Length, 0.75) # Terceiro Quartil## 75%
## 6.4
A partir desse conceito dos quartis, estamos aptos a definir a primeira medida de dispersão do nosso estudo que é a amplitude interquartil. A amplitude interquartil de um conjunto, em Estatística, é a diferença entre o terceiro e o primeiro quartil, isto é,
\[A_Q = Q_3 - Q_1\]
A grande vantagem dessa medida é que, diferente da amplitude usual (máximo – mínimo), a amplitude interquartil é mais estável, pois não considera valores mais extremos. Além de uma melhor estabilidade, a amplitude interquartil também nos traz uma informação importante sobre os dados que são os outliers. Os outliers são definidos como valores atípicos das observações de uma variável. Sendo assim, uma observação, \(x_i\), será um outlier inferior se:
\[x_i < Q_1 − 1,5 \cdot A_Q\]
e será um outlier superior se:
\[x_i > Q_3 + 1,5 \cdot A_Q\]
No R, para calcular a amplitude interquartil, podemos trabalhar
diretamente com a função IQR(). Assim, considerando a base
de dados iris disponível no R, a amplitude interquartil da
variável Sepal.Width é obtida pela rotina:
# Uso da função IQR():
# IQR(x, na.rm = FALSE, type = 7)
IQR(iris$Sepal.Width)## [1] 0.5
No entanto, para o cálculo dos outliers, não há uma função implementada em R. Neste caso, podemos, então, criar uma função para este fim da seguinte forma:
f_outliers <- function(x, inferior = TRUE)
{
# Uso da função IQR():
# IQR(x, na.rm = FALSE, type = 7)
# Uso da função quantile():
# quantile(x, probs = seq(0, 1, 0.25), na.rm = FALSE,
# names = TRUE, type = 7, digits = 7, ...)
# Uso da função which():
# which(x, arr.ind = FALSE, useNames = TRUE)
inf <- x[which(x < quantile(x, p = 0.25) - 1.5 * IQR(x))]
sup <- x[which(x > quantile(x, p = 0.75) + 1.5 * IQR(x))]
if(inferior == TRUE)
{
results <- data.frame(inf)
names(results) <- c('Outliers - Inferiores')
return(results)
}
else
{
results <- data.frame(sup)
names(results) <- c('Outliers - Superiores')
return(results)
}
}Assim, considerando a base de dados iris disponível no
R, os outliers da variável Sepal.Width são obtidos por
nossa função como:
f_outliers(iris$Sepal.Width, inferior = TRUE) # Outliers Inferiores## Outliers - Inferiores
## 1 2
f_outliers(iris$Sepal.Width, inferior = FALSE) # Outliers Superiores## Outliers - Superiores
## 1 4.4
## 2 4.1
## 3 4.2
3.4.2.3. Variância
A variância tem por objetivo mostrar o quão distante cada valor observado no conjunto de dados dados está da média. Ela pode ser calculada, matematicamente, pela seguinte expressão:
\[ S^2 = \dfrac{1}{n − 1} \sum_{i = 1}^n (x_i - \bar{x})^2\]
em que \(\bar{x}\) é a media
amostral. No R, para calcular a variância, podemos trabalhar diretamente
com a função var(). Assim, considerando a base de dados
iris disponível no R, a variância da variável
Sepal.Length é obtida pela rotina:
# Uso da função
# var(x, y = NULL, na.rm = FALSE, use)
var(iris$Sepal.Length)## [1] 0.6856935
3.4.2.4. Desvio-Padrão
O desvio-padrão é uma medida de dispersão que indica o quanto um conjunto de dados é uniforme, isto é, quanto mais próximo de 0 for o desvio-padrão, mais homogêneo é o conjunto de dados. Tal medida é descrita por:
\[ S = \sqrt{\dfrac{1}{n − 1} \sum_{i = 1}^n (x_i - \bar{x})^2}\]
em que \(\bar{x}\) é a media
amostral. No R, para calcular o desvio-padrão, podemos trabalhar
diretamente com a função sd(). Assim, considerando a base
de dados iris disponível no R, o desvio-padrão
Sepal.Length é obtido pela rotina:
# Uso da função
# sd(x, na.rm = FALSE)
sd(iris$Sepal.Length) ## [1] 0.8280661
3.4.2.5. Coeficiente de Variação
Por fim, a última medida de dispersão que temos é o coeficiente de variação (CV). O CV é conhecida por ser uma medida de dispersão pura (isto é, não possui unidade) utilizada, em geral, na comparação de grandezas com unidade de medida diferentes. Ele é calculado pela expressão:
\[ CV_\% = 100 \cdot \dfrac{s}{\bar{x}}\]
em que \(\bar{x}\) é a media
amostral, e \(s\) é o desvio-padrão
amostral. No R, não há uma função específica para o cálculo do CV, porém
podemos trabalhar com as funções mean() e
sd(). Assim, considerando a base de dados iris
disponível no R, o CV Sepal.Length é obtido pela
rotina:
# Uso das funções mean() e sd():
# mean(x, na.rm = FALSE, ...)
# sd(x, na.rm = FALSE)
# Cálculo do CV
CV <- 100 * (sd(iris$Sepal.Length) / mean(iris$Sepal.Length))
CV## [1] 14.17113
3.4.3. Medidas de Associação
Para finalizar nosso estudo das medidas descritivas, nos resta entender os conceitos das medidas de associação que são utilizadas para descrever a relação entre duas ou mais variáveis. Dentre tais medidas, as principais são: covariância, e coeficiente de correlação.
3.4.3.1. Covariância
A primeira medida de associação que temos é a covariância. Esta medida é utilizada, em geral, para medir o grau de interdependência numérica entre duas variáveis de modo que se a covariância é igual à zero, então as variáveis são independentes. Matematicamente, ela é dada pela equação:
\[\text{Cov}(X,Y) = \dfrac{1}{n-1} \sum_{i=1}^{n} (x_i - \bar{x})(y_i - \bar{y})\]
em que \(\bar{x}\) é a media
amostral da variável \(X\), e em que
\(\bar{y}\) é a media amostral da
variável \(Y\). No R, para calcular a
covariância, podemos trabalhar diretamente com a função
cov(). Assim, considerando a base de dados
iris disponível no R, a covariância entre as variáveis
Sepal.Length e Petal.Length é obtida pela
rotina:
# Uso da função
# cov(x, y = NULL, use = "everything",
# method = c("pearson", "kendall", "spearman"))
cov(x = iris$Sepal.Length, y = iris$Petal.Length)## [1] 1.274315
3.4.3.2. Coeficiente de Correlação
Uma vez definido o que é covariância, estamos aptos para definir o conceito de coeficiente de correlação de Pearson (r). Este coeficiente nada mais é que uma medida de associação que mede tanto a direção quanto a força de uma relação linear entre as variáveis X e Y. Matematicamente, esta medida é calculada pela equação:
\[r = \dfrac{\text{Cov}(X,Y)}{s_X s_Y}\]
em que \(s_X\) e \(s_Y\) são os desvios-padrões amostrais de ambas as variáveis.
Em geral, o que a correlação procura entender é como uma variável se comporta em um cenário onde outra está variando, visando identificar se existe alguma relação entre a variabilidade de ambas, e quantificando essa relação através de valores situados entre -1 e 1. Isto é, quando o \(r\) se aproxima de 1, nota-se um aumento no valor de uma variável quando a outra também aumenta (relação linear positiva). Por outro lado, quando o \(r\) se aproxima de -1, verifica-se que o valor de uma variável aumenta o da outra diminui (relação linear inversa).
No R, para calcular o coeficiente de correlação de Pearson, podemos
trabalhar diretamente com a função cor(). Assim,
considerando a base de dados iris disponível no R, o
coeficiente de correlação entre as variáveis Sepal.Length e
Petal.Length é obtida pela rotina:
# Uso da função cor():
# cor(x, y = NULL, use = "everything",
# method = c("pearson", "kendall", "spearman"))
cor(x = iris$Sepal.Length, y = iris$Petal.Length, method = 'pearson')## [1] 0.8717538
Existem vários métodos de correlação, no entanto, apenas três são
implementados na função cor() do R:
Pearson, Spearman, e
Kendall. A correlação de Pearson, em geral, é a mais
freqüentemente usada para variáveis quantitativas contínuas que possuem
uma relação linear. Por outro lado, A correlação de Spearman (que na
verdade é semelhante à de Pearson, mas baseada nos valores classificados
para cada variável, e não nos dados brutos) é frequentemente usada para
avaliar relacionamentos envolvendo pelo menos uma variável ordinal
qualitativa ou duas variáveis quantitativas se a ligação for
parcialmente linear. Por fim, a correlação de Kendall, que é calculado a
partir do número de pares concordantes e discordantes, é frequentemente
usado para variáveis ordinais qualitativas.
3.4.3.2.1. Matriz de Correlação
Suponha agora que queremos calcular correlações para vários pares de
variáveis, em vez de um par de variáveis. Dentro do R, para realizar
essa tarefa para todos os pares possíveis de variáveis no conjunto de
dados, pode-se, novamente, trabalhar com a função cor().
Para exemplificar, considere o conjunto de dados iris
disponível no R. Neste caso, para encontrar a matriz de correlação,
seguimos a rotina:
## Carregar os dados
data(iris)
## Removendo a variável 'Species' por ser categórica
iris$Species <- NULL
## Matriz de Correlação:
# Uso da função cor():
# cor(x, y = NULL, use = "everything",
# method = c("pearson", "kendall", "spearman"))
round(cor(iris), 2)## Sepal.Length Sepal.Width Petal.Length Petal.Width
## Sepal.Length 1.00 -0.12 0.87 0.82
## Sepal.Width -0.12 1.00 -0.43 -0.37
## Petal.Length 0.87 -0.43 1.00 0.96
## Petal.Width 0.82 -0.37 0.96 1.00
3.4.3.2.2. Gráfico da Matriz de Correlação
Em algumas situações, a matriz de correlação apresentada
anteriormente pode não ser facilmente interpretável, especialmente
quando o conjunto de dados é composto por muitas variáveis. Então, para
solucionar esse problema, trabalhamos com a visualização gráfica dessa
matriz por meio do gráfico que chamamos de
correlograma. No R, o correlograma pode ser construído
a partir da função corrplot() do pacote
corrplot. Para exemplificar, vamos, novamente, considerar a
base de dados iris disponível no R.
## Carregar os dados
data(iris)
## Removendo a variável 'Species' por ser categórica
iris$Species <- NULL
## Carregar o pacote:
library(corrplot)## Warning: package 'corrplot' was built under R version 4.2.2
## corrplot 0.92 loaded
## Gráfico da Matriz de Correlação:
# Uso da função corrplot():
# corrplot(corr,method = c("circle", "square", "ellipse", "number", "shade", "color", "pie"),
# type = c("full", "lower", "upper"), col = NULL, col.lim = NULL, bg = "white", title = "",
# is.corr = TRUE, add = FALSE, diag = TRUE, outline = FALSE, mar = c(0, 0, 0, 0), addgrid.col = NULL,
# addCoef.col = NULL, addCoefasPercent = FALSE, order = c("original", "AOE", "FPC", "hclust", "alphabet"),
# hclust.method = c("complete", "ward", "ward.D", "ward.D2", "single", "average", "mcquitty", "median", "centroid"),
# addrect = NULL, rect.col = "black", rect.lwd = 2, tl.pos = NULL, tl.cex = 1, tl.col = "red", tl.offset = 0.4,
# tl.srt = 90, cl.pos = NULL, cl.length = NULL, cl.cex = 0.8, cl.ratio = 0.15, cl.align.text = "c",
# cl.offset = 0.5, number.cex = 1, number.font = 2, number.digits = NULL, addshade = c("negative", "positive", "all"),
# shade.lwd = 1, shade.col = "white", p.mat = NULL, insig = c("pch", "p-value", "blank", "n", "label_sig"),
# pch = 4, pch.col = "black", pch.cex = 3, plotCI = c("n", "square", "circle", "rect"), lowCI.mat = NULL,
# uppCI.mat = NULL, na.label = "?", na.label.col = "black", win.asp = 1, sig.level = 0.05, ...)
corrplot(cor(iris),
method = "number",
type = "upper" # Mostra apenas a parte superior
)Uma outra opção de trabalhar com essa visualização gráfica é utilizar
a função ggcorrmat() do pacote ggstatsplot.
Essa função mostra os coeficientes de correlação e, se houver, as
correlações não-significativas (por padrão no nível de significância de
5% com o método de ajuste de Holm) são mostradas por uma grande cruz nos
coeficientes de correlação. Para exemplificar, vamos, novamente,
considerar a base de dados iris disponível no R.
## Carregar os dados
data(iris)
## Removendo a variável 'Species' por ser categórica
iris$Species <- NULL
## Carregar o pacote:
library(ggstatsplot)## Warning: package 'ggstatsplot' was built under R version 4.2.2
## You can cite this package as:
## Patil, I. (2021). Visualizations with statistical details: The 'ggstatsplot' approach.
## Journal of Open Source Software, 6(61), 3167, doi:10.21105/joss.03167
## Gráfico da Matriz de Correlação:
# Uso da função corrplot():
# ggcorrmat(data, cor.vars = NULL, cor.vars.names = NULL, output = "plot", matrix.type = "upper",
# type = "parametric", tr = 0.2, partial = FALSE, k = 2L, sig.level = 0.05, conf.level = 0.95,
# bf.prior = 0.707, p.adjust.method = "holm", pch = "cross",
# ggcorrplot.args = list(method = "square", outline.color = "black", pch.cex = 14),
# package = "RColorBrewer", palette = "Dark2", colors = c("#E69F00", "white", "#009E73"),
# ggtheme = ggstatsplot::theme_ggstatsplot(), ggplot.component = NULL, title = NULL,
# subtitle = NULL, caption = NULL, ...)
ggcorrmat(
data = iris,
type = "parametric", # Argumento 'parametric' deve ser utilizado para a correlação de Pearson
colors = c("darkred", "white", "steelblue")
)3.5. Tabelas de Contingência
As tabelas de contingência são formadas pelo cruzamento de duas variáveis categóricas, podendo ser de tamanho \(k \times n\), em que “k” representa o nº de linhas e “n” o de colunas. Tais tabelas podem ser aplicadas em diversas áreas, mas, frequentemente, são utilizadas no campo das ciências da saúde em estudos de testes de triagem, e no campo das ciências econômicas em estudos de modelos de crédito. No entanto, seu maior uso, ainda, é na aplicação de testes Qui-Quadrado.
No R, há várias formas de se construir uma tabela de contingência,
sendo a mais usual pela função table(). Por exemplo,
considere a base de dados referente aos dados de insuficiência cardíaca
trabalhada anteriormente e, suponha, que nosso objetivo seja criar uma
tabela de contingência referente ao sexo (variável Gender)
e pressão alta (variável BP). Neste caso, podemos seguir a
seguinte rotina em R:
## Leitura dos Dados (Dataset: Heart)
setwd('datasets')
db <- read.csv('heart.csv', header = TRUE, sep = ',')
## Reestruturação do Banco de Dados
db$Event <- ifelse(db$Event == 1, 'Non-Censored', 'Censored')
db$Gender <- ifelse(db$Gender == 1, 'Male', 'Female')
db$Smoking <- ifelse(db$Smoking == 1, 'Yes', 'No')
db$Diabetes <- ifelse(db$Diabetes == 1, 'Yes', 'No')
db$BP <- ifelse(db$BP == 1, 'Yes', 'No')
db$Anaemia <- ifelse(db$Anaemia == 1, 'Yes', 'No')
## Visualização dos dados
head(db, n = 10)## Time Event Gender Smoking Diabetes BP Anaemia Age Ejection.Fraction
## 1 97 Censored Female No No No Yes 43 50
## 2 180 Censored Male Yes Yes No Yes 73 30
## 3 31 Non-Censored Male Yes No Yes No 70 20
## 4 87 Censored Male No No No Yes 65 25
## 5 113 Censored Male No No No No 64 60
## 6 10 Non-Censored Male No No No Yes 75 15
## 7 250 Censored Male Yes No No No 70 40
## 8 27 Non-Censored Male No Yes Yes No 94 38
## 9 87 Censored Male No No Yes No 75 45
## 10 87 Censored Male Yes No No No 80 25
## Sodium Creatinine Pletelets CPK
## 1 135 1.30 237000 358
## 2 142 1.18 160000 231
## 3 134 1.83 263358 582
## 4 141 1.10 298000 305
## 5 137 1.00 242000 1610
## 6 137 1.20 127000 246
## 7 136 2.70 51000 582
## 8 134 1.83 263358 582
## 9 137 1.18 263358 582
## 10 144 1.10 149000 898
## Tabela de Contingência 2x2: Sexo x Pressão Alta
# Uso da função table()
# table(..., exclude = if (useNA == "no") c(NA, NaN),
# useNA = c("no", "ifany", "always"),
# dnn = list.names(...), deparse.level = 1)
table(db$Gender, db$BP)##
## No Yes
## Female 61 44
## Male 133 61
A tabela de contigência, em geral, nos informa o número de “casos” em
cada subgrupo. No entanto, em algumas situações, ao invés de trabalhar
com as frequências absolutas (ou seja, o número de “casos”), pode-se
trabalhar com as frequências relativas (ou seja, proporções) em cada
subgrupo adicionando a função table() dentro da função
prop.table(). Considerando o exemplo anterior, então,
tem-se que:
## Tabela de Contingência 2x2: Sexo x Pressão Alta
table(db$Gender, db$BP)##
## No Yes
## Female 61 44
## Male 133 61
## Tabela de Contingência 2x2: Sexo x Pressão Alta (Uso de Frequências Relativas)
# Uso da função prop.table()
# prop.table(x, margin = NULL)
prop.table(table(db$Gender, db$BP))##
## No Yes
## Female 0.2040134 0.1471572
## Male 0.4448161 0.2040134
Observação: Podemos também pode calcular as
porcentagens por linha ou por coluna adicionando um segundo argumento à
função prop.table(): 1 para linha, ou
2 para coluna:
# Porcentagens por linha:
round(prop.table(table(db$Gender, db$BP), 1), 2) # arredonda para 2 dígitos com round()##
## No Yes
## Female 0.58 0.42
## Male 0.69 0.31
# Porcentagens por coluna:
round(prop.table(table(db$Gender, db$BP), 2), 2) # arredonda para 2 dígitos com round()##
## No Yes
## Female 0.31 0.42
## Male 0.69 0.58
Uma outra forma de trabalhar com as tabelas de contingência dentro do
R é por meio da função ctable() do pacote
summarytools. A função ctable() produz
tabulações cruzadas (também conhecidas como tabelas de contingência)
para pares de variáveis categóricas, utilizando frequências absolutas e
também frequ~encias relativas (em percentual). Como exemplo, considere
novamente as duas variáveis categóricas, sexo (variável
Gender) e pressão alta (variável BP), de nosso
conjunto de dados. Logo, tem-se que:
## Carregar pacote
library(summarytools)
## Tabela de Contingência 2x2: Sexo x Pressão Alta
# Uso da função ctable()
# ctable(x, y,prop = st_options("ctable.prop"),
# useNA = "ifany", totals = st_options("ctable.totals"),
# style = st_options("style"), round.digits = st_options("ctable.round.digits"),
# justify = "right", plain.ascii = st_options("plain.ascii"),
# headings = st_options("headings"), display.labels = st_options("display.labels"),
# split.tables = Inf, dnn = c(substitute(x), substitute(y)),
# chisq = FALSE, OR = FALSE, RR = FALSE, weights = NA, rescale.weights = FALSE,
# ...)
ctable(x = db$Gender, y = db$BP)## Cross-Tabulation, Row Proportions
## Gender * BP
## Data Frame: db
##
## -------- ---- ------------- ------------- --------------
## BP No Yes Total
## Gender
## Female 61 (58.1%) 44 (41.9%) 105 (100.0%)
## Male 133 (68.6%) 61 (31.4%) 194 (100.0%)
## Total 194 (64.9%) 105 (35.1%) 299 (100.0%)
## -------- ---- ------------- ------------- --------------
## Tabela de Contingência 2x2: Sexo x Pressão Alta (Uso de Proporções Totais)
ctable(x = db$Gender, y = db$BP, prop = 't')## Cross-Tabulation, Total Proportions
## Gender * BP
## Data Frame: db
##
## -------- ---- ------------- ------------- --------------
## BP No Yes Total
## Gender
## Female 61 (20.4%) 44 (14.7%) 105 ( 35.1%)
## Male 133 (44.5%) 61 (20.4%) 194 ( 64.9%)
## Total 194 (64.9%) 105 (35.1%) 299 (100.0%)
## -------- ---- ------------- ------------- --------------
3.6. Resumos Descritivos
3.6.1. Pacote: summarytools
Um dos pacotes mais fundamentais para fazer um resumo descritivo é o
pacote summarytools. Basicamente, este pacote é centrado em
torno de 4 funções:
freq()para tabelas de frequências.ctable()para tabulações cruzadas.descr()para estatísticas descritivas.dfSummary()para resumos de data.frame.
Para o nosso estudo, nesta seção, vamos considerar a função
descr(). A função descr() produz estatísticas
descritivas (univariadas) com as medidas de posição e de dispersão. Uma
grande vantagem dessa função é que ela aceita vetores únicos, bem como
data.frames. Se um data.frame for fornecido, todas as colunas não
numéricas serão ignoradas para que você não precise removê-las antes de
executar a função. Além disso a função descr() permite
exibir:
- Apenas uma seleção de estatísticas descritivas de sua escolha, com o
argumento
stats = c("mean", "sd")para média e desvio padrão. - Mínimo, primeiro quartil, mediana, terceiro quartil e máximo com
stats = "fivenum". - As estatísticas descritivas mais comuns (média, desvio padrão,
mínimo, mediano, máximo, número e porcentagem de observações válidas),
com
stats = "common".
Para exemplificar tais conceitos, considere a base de dados
iris existente dentro software R. Neste caso, para cada uma
das opções acima, trabalhamos com as rotinas:
# Carregar o Pacote
library(summarytools)
# Carregar os Dados
data(iris)
# Uso da funçã descr():
# descr(x, var = NULL, stats = st_options("descr.stats"), na.rm = TRUE, round.digits = st_options("round.digits"),
# transpose = st_options("descr.transpose"), order = "sort", style = st_options("style"),
# plain.ascii = st_options("plain.ascii"), justify = "r", headings = st_options("headings"),
# display.labels = st_options("display.labels"), split.tables = 100, weights = NA, rescale.weights = FALSE,
# ...)
# Resumo Descritivo: 'mean' e 'sd'
descr(iris, headings = FALSE, stats = c("mean", "sd"))## Non-numerical variable(s) ignored: Species
##
## Petal.Length Petal.Width Sepal.Length Sepal.Width
## ------------- -------------- ------------- -------------- -------------
## Mean 3.76 1.20 5.84 3.06
## Std.Dev 1.77 0.76 0.83 0.44
# Resumo Descritivo: 'fivenum'
descr(iris, headings = FALSE, stats = "fivenum")## Non-numerical variable(s) ignored: Species
##
## Petal.Length Petal.Width Sepal.Length Sepal.Width
## ------------ -------------- ------------- -------------- -------------
## Min 1.00 0.10 4.30 2.00
## Q1 1.60 0.30 5.10 2.80
## Median 4.35 1.30 5.80 3.00
## Q3 5.10 1.80 6.40 3.30
## Max 6.90 2.50 7.90 4.40
# Resumo Descritivo: 'common'
descr(iris, headings = FALSE, stats = "common")## Non-numerical variable(s) ignored: Species
##
## Petal.Length Petal.Width Sepal.Length Sepal.Width
## --------------- -------------- ------------- -------------- -------------
## Mean 3.76 1.20 5.84 3.06
## Std.Dev 1.77 0.76 0.83 0.44
## Min 1.00 0.10 4.30 2.00
## Median 4.35 1.30 5.80 3.00
## Max 6.90 2.50 7.90 4.40
## N.Valid 150.00 150.00 150.00 150.00
## Pct.Valid 100.00 100.00 100.00 100.00
Em algumas situações, podemos estar interessados em resumos
descritivos por grupos. Neste caso, para calcular essas estatísticas
descritivas por grupo, podemos, então, usar a função
descr() em combinação com a função stby()
(responsável para trabalhar com dados divididos em grupos) do pacote
summarytools. Para exemplificar, consideremos a base de
dados heart descrita anteriormente.
# Carregar o pacote
library(summarytools)
## Leitura dos Dados (Dataset: Heart)
setwd('datasets')
db <- read.csv('heart.csv', header = TRUE, sep = ',')
## Reestruturação do Banco de Dados
db$Event <- ifelse(db$Event == 1, 'Non-Censored', 'Censored')
db$Gender <- ifelse(db$Gender == 1, 'Male', 'Female')
db$Smoking <- ifelse(db$Smoking == 1, 'Yes', 'No')
db$Diabetes <- ifelse(db$Diabetes == 1, 'Yes', 'No')
db$BP <- ifelse(db$BP == 1, 'Yes', 'No')
db$Anaemia <- ifelse(db$Anaemia == 1, 'Yes', 'No')
# Visualização dos dados
head(db, n = 10)## Time Event Gender Smoking Diabetes BP Anaemia Age Ejection.Fraction
## 1 97 Censored Female No No No Yes 43 50
## 2 180 Censored Male Yes Yes No Yes 73 30
## 3 31 Non-Censored Male Yes No Yes No 70 20
## 4 87 Censored Male No No No Yes 65 25
## 5 113 Censored Male No No No No 64 60
## 6 10 Non-Censored Male No No No Yes 75 15
## 7 250 Censored Male Yes No No No 70 40
## 8 27 Non-Censored Male No Yes Yes No 94 38
## 9 87 Censored Male No No Yes No 75 45
## 10 87 Censored Male Yes No No No 80 25
## Sodium Creatinine Pletelets CPK
## 1 135 1.30 237000 358
## 2 142 1.18 160000 231
## 3 134 1.83 263358 582
## 4 141 1.10 298000 305
## 5 137 1.00 242000 1610
## 6 137 1.20 127000 246
## 7 136 2.70 51000 582
## 8 134 1.83 263358 582
## 9 137 1.18 263358 582
## 10 144 1.10 149000 898
# Uso da função stby():
# stby(data, INDICES, FUN, ..., simplify = TRUE)
stby(data = db,
INDICES = db$Gender, # por sexo
FUN = descr, # resumo descritivo
stats = "common",
transpose = TRUE)## Non-numerical variable(s) ignored: Event, Gender, Smoking, Diabetes, BP, Anaemia
## Descriptive Statistics
## db
## Group: Gender = Female
## N: 105
##
## Mean Std.Dev Min Median Max N.Valid Pct.Valid
## ----------------------- ----------- ----------- ---------- ----------- ----------- --------- -----------
## Age 59.78 11.24 40.00 60.00 95.00 105.00 100.00
## CPK 476.78 611.36 52.00 250.00 3964.00 105.00 100.00
## Creatinine 1.38 1.12 0.50 1.00 9.00 105.00 100.00
## Ejection.Fraction 40.47 12.73 15.00 38.00 80.00 105.00 100.00
## Pletelets 279964.02 102108.75 62000.00 263358.03 742000.00 105.00 100.00
## Sodium 136.79 4.90 116.00 137.00 146.00 105.00 100.00
## Time 131.90 77.63 8.00 109.00 278.00 105.00 100.00
##
## Group: Gender = Male
## N: 194
##
## Mean Std.Dev Min Median Max N.Valid Pct.Valid
## ----------------------- ----------- ---------- ---------- ----------- ----------- --------- -----------
## Age 61.41 12.22 40.00 60.00 95.00 194.00 100.00
## CPK 638.70 1114.89 23.00 249.00 7861.00 194.00 100.00
## Creatinine 1.40 0.99 0.60 1.10 9.40 194.00 100.00
## Ejection.Fraction 36.79 11.14 14.00 35.00 62.00 194.00 100.00
## Pletelets 254370.25 94447.36 25100.00 253000.00 850000.00 194.00 100.00
## Sodium 136.54 4.13 113.00 137.00 148.00 194.00 100.00
## Time 129.37 77.79 4.00 117.50 285.00 194.00 100.00
Além das opções acima, dentro do pacote summarytools,
pode ser que nosso interesse seja resumir dados contidos apenas na
estrutura de data.frame(). Neste caso, pode-se trabalhar
com a função dfSummary() do pacote
summarytools. Para exemplificar, consideremos a base de
dados iris disponível no R, e, também, a base de dados
heart apresentada anteriormente.
# Carregar o Pacote
library(summarytools)
# Carregar os Dados
data(iris)
# Uso da função dfSummary():
# dfSummary(x, round.digits = 1, varnumbers = st_options("dfSummary.varnumbers"),
# labels.col = st_options("dfSummary.labels.col"),
# valid.col = st_options("dfSummary.valid.col"),
# na.col = st_options("dfSummary.na.col"),
# graph.col = st_options("dfSummary.graph.col"),
# graph.magnif = st_options("dfSummary.graph.magnif"),
# style = st_options("dfSummary.style"),
# plain.ascii = st_options("plain.ascii"),
# justify = "l", col.widths = NA, headings = st_options("headings"),
# display.labels = st_options("display.labels"),
# max.distinct.values = 10, trim.strings = FALSE,
# max.string.width = 25, split.cells = 40,
# split.tables = Inf, tmp.img.dir = st_options("tmp.img.dir"),
# keep.grp.vars = FALSE, silent = st_options("dfSummary.silent"),
# ...)
dfSummary(iris)## Data Frame Summary
## iris
## Dimensions: 150 x 5
## Duplicates: 1
##
## -----------------------------------------------------------------------------------------------------------
## No Variable Stats / Values Freqs (% of Valid) Graph Valid Missing
## ---- -------------- ----------------------- -------------------- --------------------- ---------- ---------
## 1 Sepal.Length Mean (sd) : 5.8 (0.8) 35 distinct values . . : : 150 0
## [numeric] min < med < max: : : : : (100.0%) (0.0%)
## 4.3 < 5.8 < 7.9 : : : : :
## IQR (CV) : 1.3 (0.1) : : : : :
## : : : : : : : :
##
## 2 Sepal.Width Mean (sd) : 3.1 (0.4) 23 distinct values : 150 0
## [numeric] min < med < max: : (100.0%) (0.0%)
## 2 < 3 < 4.4 . :
## IQR (CV) : 0.5 (0.1) : : : :
## . . : : : : : :
##
## 3 Petal.Length Mean (sd) : 3.8 (1.8) 43 distinct values : 150 0
## [numeric] min < med < max: : . : (100.0%) (0.0%)
## 1 < 4.3 < 6.9 : : : .
## IQR (CV) : 3.5 (0.5) : : : : : .
## : : . : : : : : .
##
## 4 Petal.Width Mean (sd) : 1.2 (0.8) 22 distinct values : 150 0
## [numeric] min < med < max: : (100.0%) (0.0%)
## 0.1 < 1.3 < 2.5 : . . :
## IQR (CV) : 1.5 (0.6) : : : : .
## : : : : : . : : :
##
## 5 Species 1. setosa 50 (33.3%) IIIIII 150 0
## [factor] 2. versicolor 50 (33.3%) IIIIII (100.0%) (0.0%)
## 3. virginica 50 (33.3%) IIIIII
## -----------------------------------------------------------------------------------------------------------
# Carregar o pacote
library(summarytools)
## Leitura dos Dados (Dataset: Heart)
setwd('datasets')
db <- read.csv('heart.csv', header = TRUE, sep = ',')
## Reestruturação do Banco de Dados
db$Event <- ifelse(db$Event == 1, 'Non-Censored', 'Censored')
db$Gender <- ifelse(db$Gender == 1, 'Male', 'Female')
db$Smoking <- ifelse(db$Smoking == 1, 'Yes', 'No')
db$Diabetes <- ifelse(db$Diabetes == 1, 'Yes', 'No')
db$BP <- ifelse(db$BP == 1, 'Yes', 'No')
db$Anaemia <- ifelse(db$Anaemia == 1, 'Yes', 'No')
# Visualização dos dados
head(db, n = 10)## Time Event Gender Smoking Diabetes BP Anaemia Age Ejection.Fraction
## 1 97 Censored Female No No No Yes 43 50
## 2 180 Censored Male Yes Yes No Yes 73 30
## 3 31 Non-Censored Male Yes No Yes No 70 20
## 4 87 Censored Male No No No Yes 65 25
## 5 113 Censored Male No No No No 64 60
## 6 10 Non-Censored Male No No No Yes 75 15
## 7 250 Censored Male Yes No No No 70 40
## 8 27 Non-Censored Male No Yes Yes No 94 38
## 9 87 Censored Male No No Yes No 75 45
## 10 87 Censored Male Yes No No No 80 25
## Sodium Creatinine Pletelets CPK
## 1 135 1.30 237000 358
## 2 142 1.18 160000 231
## 3 134 1.83 263358 582
## 4 141 1.10 298000 305
## 5 137 1.00 242000 1610
## 6 137 1.20 127000 246
## 7 136 2.70 51000 582
## 8 134 1.83 263358 582
## 9 137 1.18 263358 582
## 10 144 1.10 149000 898
# Uso da função dfSummary():
# dfSummary(x, round.digits = 1, varnumbers = st_options("dfSummary.varnumbers"),
# labels.col = st_options("dfSummary.labels.col"),
# valid.col = st_options("dfSummary.valid.col"),
# na.col = st_options("dfSummary.na.col"),
# graph.col = st_options("dfSummary.graph.col"),
# graph.magnif = st_options("dfSummary.graph.magnif"),
# style = st_options("dfSummary.style"),
# plain.ascii = st_options("plain.ascii"),
# justify = "l", col.widths = NA, headings = st_options("headings"),
# display.labels = st_options("display.labels"),
# max.distinct.values = 10, trim.strings = FALSE,
# max.string.width = 25, split.cells = 40,
# split.tables = Inf, tmp.img.dir = st_options("tmp.img.dir"),
# keep.grp.vars = FALSE, silent = st_options("dfSummary.silent"),
# ...)
dfSummary(db)## Data Frame Summary
## db
## Dimensions: 299 x 13
## Duplicates: 0
##
## ------------------------------------------------------------------------------------------------------------------------
## No Variable Stats / Values Freqs (% of Valid) Graph Valid Missing
## ---- ------------------- ------------------------------ --------------------- --------------------- ---------- ---------
## 1 Time Mean (sd) : 130.3 (77.6) 148 distinct values : . 299 0
## [integer] min < med < max: : : : : (100.0%) (0.0%)
## 4 < 115 < 285 : : : : :
## IQR (CV) : 130 (0.6) : : : : :
## : : : : : :
##
## 2 Event 1. Censored 203 (67.9%) IIIIIIIIIIIII 299 0
## [character] 2. Non-Censored 96 (32.1%) IIIIII (100.0%) (0.0%)
##
## 3 Gender 1. Female 105 (35.1%) IIIIIII 299 0
## [character] 2. Male 194 (64.9%) IIIIIIIIIIII (100.0%) (0.0%)
##
## 4 Smoking 1. No 203 (67.9%) IIIIIIIIIIIII 299 0
## [character] 2. Yes 96 (32.1%) IIIIII (100.0%) (0.0%)
##
## 5 Diabetes 1. No 174 (58.2%) IIIIIIIIIII 299 0
## [character] 2. Yes 125 (41.8%) IIIIIIII (100.0%) (0.0%)
##
## 6 BP 1. No 194 (64.9%) IIIIIIIIIIII 299 0
## [character] 2. Yes 105 (35.1%) IIIIIII (100.0%) (0.0%)
##
## 7 Anaemia 1. No 170 (56.9%) IIIIIIIIIII 299 0
## [character] 2. Yes 129 (43.1%) IIIIIIII (100.0%) (0.0%)
##
## 8 Age Mean (sd) : 60.8 (11.9) 47 distinct values : 299 0
## [numeric] min < med < max: . : . . (100.0%) (0.0%)
## 40 < 60 < 95 : : : : : :
## IQR (CV) : 19 (0.2) : : : : : :
## : : : : : : : : . .
##
## 9 Ejection.Fraction Mean (sd) : 38.1 (11.8) 17 distinct values : 299 0
## [integer] min < med < max: : (100.0%) (0.0%)
## 14 < 38 < 80 :
## IQR (CV) : 15 (0.3) . . : .
## : : : : : : :
##
## 10 Sodium Mean (sd) : 136.6 (4.4) 27 distinct values : 299 0
## [integer] min < med < max: : (100.0%) (0.0%)
## 113 < 137 < 148 . :
## IQR (CV) : 6 (0) : : .
## . : : :
##
## 11 Creatinine Mean (sd) : 1.4 (1) 40 distinct values : 299 0
## [numeric] min < med < max: : (100.0%) (0.0%)
## 0.5 < 1.1 < 9.4 :
## IQR (CV) : 0.5 (0.7) :
## : : .
##
## 12 Pletelets Mean (sd) : 263358 (97804.2) 176 distinct values : 299 0
## [numeric] min < med < max: : (100.0%) (0.0%)
## 25100 < 262000 < 850000 :
## IQR (CV) : 91000 (0.4) : : :
## . : : : .
##
## 13 CPK Mean (sd) : 581.8 (970.3) 208 distinct values : 299 0
## [integer] min < med < max: : (100.0%) (0.0%)
## 23 < 250 < 7861 :
## IQR (CV) : 465.5 (1.7) :
## : .
## ------------------------------------------------------------------------------------------------------------------------
3.6.2. Pacote: psych
Um outro pacote que é muito útil para fazer um resumo descritivo é o
pacote psych. Neste pacote, a função
describeBy() deste pacote nos permite relatar um grande rol
de medidas descritivas como, por exemplo, número de observações válidas,
média, desvio-padrão, mediana, desvio absoluto (em relação a mediana),
assimetria, curtose, entre outras. Além disso, também permite o uso de
variáveis de grupo para exibir o resumo descritivo. Para exemplificar,
vamos considerar a base de dados iris disponível no R.
# Carregar o Pacote
library(psych)##
## Attaching package: 'psych'
## The following objects are masked from 'package:ggplot2':
##
## %+%, alpha
## The following objects are masked from 'package:epiDisplay':
##
## alpha, cs, lookup
# Carregar os Dados
data(iris)
# Uso da função describeBy():
describeBy(iris, iris$Species)##
## Descriptive statistics by group
## group: setosa
## vars n mean sd median trimmed mad min max range skew kurtosis
## Sepal.Length 1 50 5.01 0.35 5.0 5.00 0.30 4.3 5.8 1.5 0.11 -0.45
## Sepal.Width 2 50 3.43 0.38 3.4 3.42 0.37 2.3 4.4 2.1 0.04 0.60
## Petal.Length 3 50 1.46 0.17 1.5 1.46 0.15 1.0 1.9 0.9 0.10 0.65
## Petal.Width 4 50 0.25 0.11 0.2 0.24 0.00 0.1 0.6 0.5 1.18 1.26
## Species* 5 50 1.00 0.00 1.0 1.00 0.00 1.0 1.0 0.0 NaN NaN
## se
## Sepal.Length 0.05
## Sepal.Width 0.05
## Petal.Length 0.02
## Petal.Width 0.01
## Species* 0.00
## ------------------------------------------------------------
## group: versicolor
## vars n mean sd median trimmed mad min max range skew kurtosis
## Sepal.Length 1 50 5.94 0.52 5.90 5.94 0.52 4.9 7.0 2.1 0.10 -0.69
## Sepal.Width 2 50 2.77 0.31 2.80 2.78 0.30 2.0 3.4 1.4 -0.34 -0.55
## Petal.Length 3 50 4.26 0.47 4.35 4.29 0.52 3.0 5.1 2.1 -0.57 -0.19
## Petal.Width 4 50 1.33 0.20 1.30 1.32 0.22 1.0 1.8 0.8 -0.03 -0.59
## Species* 5 50 2.00 0.00 2.00 2.00 0.00 2.0 2.0 0.0 NaN NaN
## se
## Sepal.Length 0.07
## Sepal.Width 0.04
## Petal.Length 0.07
## Petal.Width 0.03
## Species* 0.00
## ------------------------------------------------------------
## group: virginica
## vars n mean sd median trimmed mad min max range skew kurtosis
## Sepal.Length 1 50 6.59 0.64 6.50 6.57 0.59 4.9 7.9 3.0 0.11 -0.20
## Sepal.Width 2 50 2.97 0.32 3.00 2.96 0.30 2.2 3.8 1.6 0.34 0.38
## Petal.Length 3 50 5.55 0.55 5.55 5.51 0.67 4.5 6.9 2.4 0.52 -0.37
## Petal.Width 4 50 2.03 0.27 2.00 2.03 0.30 1.4 2.5 1.1 -0.12 -0.75
## Species* 5 50 3.00 0.00 3.00 3.00 0.00 3.0 3.0 0.0 NaN NaN
## se
## Sepal.Length 0.09
## Sepal.Width 0.05
## Petal.Length 0.08
## Petal.Width 0.04
## Species* 0.00
3.7. O Uso do tidyverse
O tidyverse é um conjunto de pacotes que tornam o R mais
fácil de usar. Todos os pacotes trabalham juntos e compartilham uma
gramática e filosofia subjacentes. Isso mesmo - filosofia. O tidyverse
opera na suposição de que os dados devem ser “arrumados”. De acordo com
Hadley Wickham, cientista-chefe do RStudio e um dos criadores do
tidyverse:
Dados organizados são uma maneira
padrão de mapear o significado de um conjunto de dados para sua
estrutura. Um conjunto de dados é classificado como
confuso ou organizado dependendo de
como as linhas, colunas e tabelas são combinadas com as observações e as
variáveis.
Em geral, os dados são ditos organizados se:
- Cada variável forma uma coluna.
- Cada observação forma uma linha.
- Cada tipo de unidade observacional forma uma tabela.
Observação: O tidyverse não apenas ajuda a manter os dados “arrumados”, mas torna a programação mais fácil em comparação com a sintaxe básica do R.
3.7.1. Extraindo/Criando Variáveis
Para iniciar nossos estudos sobre o tidyverse,
consideremos a base de dados iris disponível no R. A partir
dessa base de dados, iniciemos, então, com a seleção/extração de uma
variável no tidyverse de acordo com a seguinte rotina em
R:
## Carregar o pacote
library(tidyverse)## Warning: package 'tidyverse' was built under R version 4.2.2
## ── Attaching packages ─────────────────────────────────────── tidyverse 1.3.2 ──
## ✔ tibble 3.1.7 ✔ purrr 0.3.4
## ✔ tidyr 1.2.0 ✔ stringr 1.4.0
## ✔ readr 2.1.2 ✔ forcats 0.5.1
## ── Conflicts ────────────────────────────────────────── tidyverse_conflicts() ──
## ✖ psych::%+%() masks ggplot2::%+%()
## ✖ psych::alpha() masks ggplot2::alpha(), epiDisplay::alpha()
## ✖ plotly::filter() masks dplyr::filter(), stats::filter()
## ✖ dplyr::first() masks xts::first()
## ✖ dplyr::lag() masks stats::lag()
## ✖ dplyr::last() masks xts::last()
## ✖ plotly::select() masks dplyr::select(), MASS::select()
## ✖ tibble::view() masks summarytools::view()
## Carregar os dados
data(iris)
## Seleção/Extração de variáveis
# Uso da função select():
# select(.data, ...) # Para ler mais sobre a função, usar help(select)
select(iris, Species, Petal.Width) # por nome## Species Petal.Width
## 1 setosa 0.2
## 2 setosa 0.2
## 3 setosa 0.2
## 4 setosa 0.2
## 5 setosa 0.2
## 6 setosa 0.4
## 7 setosa 0.3
## 8 setosa 0.2
## 9 setosa 0.2
## 10 setosa 0.1
## 11 setosa 0.2
## 12 setosa 0.2
## 13 setosa 0.1
## 14 setosa 0.1
## 15 setosa 0.2
## 16 setosa 0.4
## 17 setosa 0.4
## 18 setosa 0.3
## 19 setosa 0.3
## 20 setosa 0.3
## 21 setosa 0.2
## 22 setosa 0.4
## 23 setosa 0.2
## 24 setosa 0.5
## 25 setosa 0.2
## 26 setosa 0.2
## 27 setosa 0.4
## 28 setosa 0.2
## 29 setosa 0.2
## 30 setosa 0.2
## 31 setosa 0.2
## 32 setosa 0.4
## 33 setosa 0.1
## 34 setosa 0.2
## 35 setosa 0.2
## 36 setosa 0.2
## 37 setosa 0.2
## 38 setosa 0.1
## 39 setosa 0.2
## 40 setosa 0.2
## 41 setosa 0.3
## 42 setosa 0.3
## 43 setosa 0.2
## 44 setosa 0.6
## 45 setosa 0.4
## 46 setosa 0.3
## 47 setosa 0.2
## 48 setosa 0.2
## 49 setosa 0.2
## 50 setosa 0.2
## 51 versicolor 1.4
## 52 versicolor 1.5
## 53 versicolor 1.5
## 54 versicolor 1.3
## 55 versicolor 1.5
## 56 versicolor 1.3
## 57 versicolor 1.6
## 58 versicolor 1.0
## 59 versicolor 1.3
## 60 versicolor 1.4
## 61 versicolor 1.0
## 62 versicolor 1.5
## 63 versicolor 1.0
## 64 versicolor 1.4
## 65 versicolor 1.3
## 66 versicolor 1.4
## 67 versicolor 1.5
## 68 versicolor 1.0
## 69 versicolor 1.5
## 70 versicolor 1.1
## 71 versicolor 1.8
## 72 versicolor 1.3
## 73 versicolor 1.5
## 74 versicolor 1.2
## 75 versicolor 1.3
## 76 versicolor 1.4
## 77 versicolor 1.4
## 78 versicolor 1.7
## 79 versicolor 1.5
## 80 versicolor 1.0
## 81 versicolor 1.1
## 82 versicolor 1.0
## 83 versicolor 1.2
## 84 versicolor 1.6
## 85 versicolor 1.5
## 86 versicolor 1.6
## 87 versicolor 1.5
## 88 versicolor 1.3
## 89 versicolor 1.3
## 90 versicolor 1.3
## 91 versicolor 1.2
## 92 versicolor 1.4
## 93 versicolor 1.2
## 94 versicolor 1.0
## 95 versicolor 1.3
## 96 versicolor 1.2
## 97 versicolor 1.3
## 98 versicolor 1.3
## 99 versicolor 1.1
## 100 versicolor 1.3
## 101 virginica 2.5
## 102 virginica 1.9
## 103 virginica 2.1
## 104 virginica 1.8
## 105 virginica 2.2
## 106 virginica 2.1
## 107 virginica 1.7
## 108 virginica 1.8
## 109 virginica 1.8
## 110 virginica 2.5
## 111 virginica 2.0
## 112 virginica 1.9
## 113 virginica 2.1
## 114 virginica 2.0
## 115 virginica 2.4
## 116 virginica 2.3
## 117 virginica 1.8
## 118 virginica 2.2
## 119 virginica 2.3
## 120 virginica 1.5
## 121 virginica 2.3
## 122 virginica 2.0
## 123 virginica 2.0
## 124 virginica 1.8
## 125 virginica 2.1
## 126 virginica 1.8
## 127 virginica 1.8
## 128 virginica 1.8
## 129 virginica 2.1
## 130 virginica 1.6
## 131 virginica 1.9
## 132 virginica 2.0
## 133 virginica 2.2
## 134 virginica 1.5
## 135 virginica 1.4
## 136 virginica 2.3
## 137 virginica 2.4
## 138 virginica 1.8
## 139 virginica 1.8
## 140 virginica 2.1
## 141 virginica 2.4
## 142 virginica 2.3
## 143 virginica 1.9
## 144 virginica 2.3
## 145 virginica 2.5
## 146 virginica 2.3
## 147 virginica 1.9
## 148 virginica 2.0
## 149 virginica 2.3
## 150 virginica 1.8
select(iris, 5, 4) # pelo índice da coluna## Species Petal.Width
## 1 setosa 0.2
## 2 setosa 0.2
## 3 setosa 0.2
## 4 setosa 0.2
## 5 setosa 0.2
## 6 setosa 0.4
## 7 setosa 0.3
## 8 setosa 0.2
## 9 setosa 0.2
## 10 setosa 0.1
## 11 setosa 0.2
## 12 setosa 0.2
## 13 setosa 0.1
## 14 setosa 0.1
## 15 setosa 0.2
## 16 setosa 0.4
## 17 setosa 0.4
## 18 setosa 0.3
## 19 setosa 0.3
## 20 setosa 0.3
## 21 setosa 0.2
## 22 setosa 0.4
## 23 setosa 0.2
## 24 setosa 0.5
## 25 setosa 0.2
## 26 setosa 0.2
## 27 setosa 0.4
## 28 setosa 0.2
## 29 setosa 0.2
## 30 setosa 0.2
## 31 setosa 0.2
## 32 setosa 0.4
## 33 setosa 0.1
## 34 setosa 0.2
## 35 setosa 0.2
## 36 setosa 0.2
## 37 setosa 0.2
## 38 setosa 0.1
## 39 setosa 0.2
## 40 setosa 0.2
## 41 setosa 0.3
## 42 setosa 0.3
## 43 setosa 0.2
## 44 setosa 0.6
## 45 setosa 0.4
## 46 setosa 0.3
## 47 setosa 0.2
## 48 setosa 0.2
## 49 setosa 0.2
## 50 setosa 0.2
## 51 versicolor 1.4
## 52 versicolor 1.5
## 53 versicolor 1.5
## 54 versicolor 1.3
## 55 versicolor 1.5
## 56 versicolor 1.3
## 57 versicolor 1.6
## 58 versicolor 1.0
## 59 versicolor 1.3
## 60 versicolor 1.4
## 61 versicolor 1.0
## 62 versicolor 1.5
## 63 versicolor 1.0
## 64 versicolor 1.4
## 65 versicolor 1.3
## 66 versicolor 1.4
## 67 versicolor 1.5
## 68 versicolor 1.0
## 69 versicolor 1.5
## 70 versicolor 1.1
## 71 versicolor 1.8
## 72 versicolor 1.3
## 73 versicolor 1.5
## 74 versicolor 1.2
## 75 versicolor 1.3
## 76 versicolor 1.4
## 77 versicolor 1.4
## 78 versicolor 1.7
## 79 versicolor 1.5
## 80 versicolor 1.0
## 81 versicolor 1.1
## 82 versicolor 1.0
## 83 versicolor 1.2
## 84 versicolor 1.6
## 85 versicolor 1.5
## 86 versicolor 1.6
## 87 versicolor 1.5
## 88 versicolor 1.3
## 89 versicolor 1.3
## 90 versicolor 1.3
## 91 versicolor 1.2
## 92 versicolor 1.4
## 93 versicolor 1.2
## 94 versicolor 1.0
## 95 versicolor 1.3
## 96 versicolor 1.2
## 97 versicolor 1.3
## 98 versicolor 1.3
## 99 versicolor 1.1
## 100 versicolor 1.3
## 101 virginica 2.5
## 102 virginica 1.9
## 103 virginica 2.1
## 104 virginica 1.8
## 105 virginica 2.2
## 106 virginica 2.1
## 107 virginica 1.7
## 108 virginica 1.8
## 109 virginica 1.8
## 110 virginica 2.5
## 111 virginica 2.0
## 112 virginica 1.9
## 113 virginica 2.1
## 114 virginica 2.0
## 115 virginica 2.4
## 116 virginica 2.3
## 117 virginica 1.8
## 118 virginica 2.2
## 119 virginica 2.3
## 120 virginica 1.5
## 121 virginica 2.3
## 122 virginica 2.0
## 123 virginica 2.0
## 124 virginica 1.8
## 125 virginica 2.1
## 126 virginica 1.8
## 127 virginica 1.8
## 128 virginica 1.8
## 129 virginica 2.1
## 130 virginica 1.6
## 131 virginica 1.9
## 132 virginica 2.0
## 133 virginica 2.2
## 134 virginica 1.5
## 135 virginica 1.4
## 136 virginica 2.3
## 137 virginica 2.4
## 138 virginica 1.8
## 139 virginica 1.8
## 140 virginica 2.1
## 141 virginica 2.4
## 142 virginica 2.3
## 143 virginica 1.9
## 144 virginica 2.3
## 145 virginica 2.5
## 146 virginica 2.3
## 147 virginica 1.9
## 148 virginica 2.0
## 149 virginica 2.3
## 150 virginica 1.8
Por outro lado, pode-se também trabalhar com a criação de novas
colunas. Neste caso, considerando a base de dados iris,
vamos criar as colunas Petal.Ratio e
Sepal.Ratio por meio da função mutate() do
tidyverse. Isto é,
## Carregar os dados
data(iris)
## Criação de novas variáveis
# Uso da função mutate():
# mutate(.data, ..., .keep = c("all", "used", "unused", "none"),
# .before = NULL, .after = NULL)
iris <- iris %>%
mutate(Petal.Ratio = Petal.Length/Petal.Width,
Sepal.Ratio = Sepal.Length/Sepal.Width)
head(iris)## Sepal.Length Sepal.Width Petal.Length Petal.Width Species Petal.Ratio
## 1 5.1 3.5 1.4 0.2 setosa 7.00
## 2 4.9 3.0 1.4 0.2 setosa 7.00
## 3 4.7 3.2 1.3 0.2 setosa 6.50
## 4 4.6 3.1 1.5 0.2 setosa 7.50
## 5 5.0 3.6 1.4 0.2 setosa 7.00
## 6 5.4 3.9 1.7 0.4 setosa 4.25
## Sepal.Ratio
## 1 1.457143
## 2 1.633333
## 3 1.468750
## 4 1.483871
## 5 1.388889
## 6 1.384615
3.7.2. Estatísticas Descritivas: Função
summarize()
No tidyverse, para trabalhar com as estatísticas
descritivas, podemos utilizar a função summarize() aninhada
com a função group_by(). A função summarize()
é muito semelhante a função mutate(), porém, em vez de
anexar uma coluna ao banco de dados, ela resume e condensa todos os
dados no número de colunas especificado. Para exemplificar o
funcionamento dessa função, vamos criar um resumo descritivo da variável
Petal.Width agrupado por espécie (variável
Species) pertencente à base de dados iris:
iris %>%
group_by(Species) %>%
summarize(media = mean(Petal.Width),
mediana = median(Petal.Width),
desvio_padrao = sd(Petal.Width),
tamanho_amostra = n())## # A tibble: 3 × 5
## Species media mediana desvio_padrao tamanho_amostra
## <fct> <dbl> <dbl> <dbl> <int>
## 1 setosa 0.246 0.2 0.105 50
## 2 versicolor 1.33 1.3 0.198 50
## 3 virginica 2.03 2 0.275 50
3.8. Outras Aplicações
3.8.1. Índices de Poluição do Ar
O arquivo zip para esta atividade é “specdata.zip”. O arquivo .zip contém 332 arquivos de valores separados por vírgula (CSV) contendo dados de monitoramento de poluição para poluição do ar com base no material particulado (PM) em 332 locais nos Estados Unidos. Cada arquivo contém dados de um única estação de monitoramento e o ID de cada estação está contido no nome do arquivo. Por exemplo, os dados da estação 200 estão contidos no arquivo “200.csv”. Cada arquivo contém três variáveis:
- Date: a data da observação no formato AAAA-MM-DD (ano-mês-dia)
- sulfate: o nível de sulfato (PM) no ar naquela data (medido em microgramas por metro cúbico)
- nitrate: o nível de nitrato (PM) no ar naquela data (medido em microgramas por metro cúbico)
Para esta tarefa, você precisará descompactar este arquivo e criar o diretório ‘specdata’. Depois de descompactar o arquivo zip, não faça nenhuma modificação nos arquivos no diretório ‘specdata’. Em cada arquivo, você notará que há muitos dias em que sulfato ou nitrato (ou ambos) estão ausentes (codificados como ‘NA’). Isso é comum com dados de monitoramento de poluição do ar nos Estados Unidos.
3.8.1.1. Função pollutantmean
Escreva uma função chamada ‘pollutantmean’ que calcula a média de um poluente (sulfato ou nitrato) em uma lista específica de estaões de monitoramento. A função ‘pollutantmean’ tem três argumentos: ‘directory’, ‘pollutant’, e ‘id’. Dado um vetor com os números de ID, ‘pollutantmean’ lê os dados de material particulado das estações do diretório especificado no argumento ‘directory’ e retorna a média do poluente em todas as estações de monitorimento, ignorando quaisquer valores ausentes codificados como ‘NA’. Um protótipo da função é o seguinte:
pollutantmean <- function(directory, pollutant, id = 1:332)
{
## 'directory' é um vetor de caracteres de comprimento 1 que indica a localização dos arquivos CSV.
## 'pollutant' é um vetor de caracteres de comprimento 1 indicando o nome do poluente para o qual
## calcularemos a média; ou "sulfato" ou "nitrato".
## 'id' é um vetor inteiro que indica os números de ID das estações a serem usadas.
## Retornar a média do poluente em todas as estações da lista no vetor 'id' (ignorando os valores NA).
## NOTA: Não arredondar o resultado!
}3.8.1.2 Função complete
Escreva uma função que leia um diretório cheio de arquivos e relate o número de casos completamente observados em cada arquivo de dados. A função deve retornar um data frame onde a primeira coluna é o nome do arquivo e a segunda coluna é o número de casos completos. Segue um protótipo desta função:
complete <- function(directory, id = 1:332)
{
## 'directory' é um vetor de caracteres de comprimento 1 que indica a localização dos arquivos CSV.
## 'id' é um vetor inteiro que indica os números de ID das estações a serem usadas.
## Retorne um data.frame da seguinte forma:
## id nobs
## 1 117
## 2 1041
## ...
## onde 'id' é o ID da estação e 'nobs' é o número de casos completos.
}3.8.1.3. Função corr
Escreva uma função que pegue um diretório de arquivos de dados e um limite (threshold) para casos completos e calcule a correlação entre sulfato e nitrato para locais de monitoramento onde o número de casos completamente observados (em todas as variáveis) é maior que o limite (threshold). A função deve retornar um vetor de correlações para as estações que atendem ao requisito do limite (threshold). Se nenhuma estação atender ao requisito de limite (threshold), a função deve retornar um vetor numérico de comprimento 0. Segue um protótipo dessa função:
corr <- function(directory, threshold = 0)
{
## 'directory' é um vetor de caracteres de comprimento 1 que indica a localização dos arquivos CSV.
## 'threshold' é um vetor numérico de comprimento 1 que indica o número de observações completamente observadas
## (em todas as variáveis) necessárias para calcular a correlação entre nitrato e sulfato; o padrão é 0.
## Retorne um vetor numérico com as correlações.
## NOTA: Não arredondar o resultado!
}3.8.2. Rank de Hospitais
Os dados para esta tarefa vêm do site Hospital Compare (http://hospitalcompare.hhs.gov) administrado pelo Departamento de Saúde e Serviços Humanos dos EUA. O objetivo do site é fornecer dados e informações sobre a qualidade do atendimento em mais de 4.000 hospitais certificados pelo Medicare nos EUA. Esse conjunto de dados cobre essencialmente todos os principais hospitais dos EUA, sendo utilizado para diversas finalidades, incluindo determinar se os hospitais devem ser multados por não fornecerem atendimento de alta qualidade aos pacientes (consulte http://goo.gl/jAXFX para obter informações sobre esse tópico específico).
O site do Hospital Compare contém muitos dados e veremos apenas um pequeno subconjunto para esta tarefa. O arquivo zip para esta tarefa contém dois arquivos:
- result-of-care-measures.csv: Contém informações sobre mortalidade em 30 dias e taxas de readmissão por ataques cardíacos, insuficiência cardíaca e pneumonia para mais de 4.000 hospitais.
- hospital-data.csv: Contém informações sobre cada hospital.
Para esta tarefa, você precisará descompactar este arquivo e criar o diretório ‘hospitaldata’. Depois de descompactar o arquivo zip, não faça nenhuma modificação nos arquivos no diretório ‘hospitaldata’.
3.8.2.1. Função best
Escreva uma função chamada ‘best’ que receba dois argumentos: o nome do estado abreviado em 2 caracteres e o nome do resultado. A função lê o arquivo outcome-of-care-measures.csv e retorna um vetor de caracteres com o nome do hospital que tem a melhor (ou seja, a menor) mortalidade em 30 dias para o resultado especificado naquele estado. O nome do hospital é o nome fornecido na variável Hospital.Name. Os resultados podem ser “heart attack”, “heart failure” ou “pneumonia”. Hospitais que não possuem dados sobre determinado desfecho devem ser excluídos do conjunto de hospitais ao decidir os rankings.
Se houver empate no melhor hospital para um determinado resultado, os nomes dos hospitais devem ser classificados em ordem alfabética e o primeiro hospital desse conjunto deve ser escolhido (ou seja, se os hospitais “b”, “c” e “f” estão empatados então o hospital “b” deve ser devolvido). A função deve usar o seguinte modelo:
best <- function(state, outcome)
{
## Ler dados de resultados.
## Verifique se o estado e o resultado são válidos.
## Retornar o nome do hospital naquele estado com menor taxa de mortalidade em 30 dias.
}A função deve verificar a validade de seus argumentos. Se o argumento
state for inválido a função deve gerar um erro por meio de
parada com a mensagem “invalid state”. Se o argumento ‘outcome’ for
inválido a função deve gerar um erro por meio de parada com a mensagem
“invalid outcome”.
3.8.2.2. Função rankhospital
Escreva uma função chamada rankhospital que receba três
argumentos: o nome abreviado de 2 caracteres de um estado (state), um
resultado (outcome) e a classificação do hospital nesse estado para esse
resultado (num). A função deve ler o arquivo
outcome-of-care-measures.csv e retornar um vetor de
caracteres com o nome do hospital que possui a classificação
especificada pelo argumento num. Por exemplo:
# rankhospital("MD", "heart failure", 5) Retornando um vetor de caracteres contendo o nome do hospital com a 5a menor taxa de mortalidade em 30 dias por insuficiência cardíaca. O argumento ‘num’ pode receber valores “melhor”, “pior” ou um número inteiro indicando a classificação (números menores são melhores). Se o número dado por num for maior que o número de hospitais naquele estado, então a função deve retornar NA. Hospitais que não possuem dados sobre determinado desfecho devem ser excluídos do conjunto de hospitais ao decidir os rankings.
Pode ocorrer que vários hospitais tenham a mesma taxa de mortalidade em 30 dias para uma determinada causa de morte. Nesses casos, os empates devem ser desfeitos com o uso do nome do hospital. Por exemplo, no Texas (“TX”), os hospitais com menor taxa de mortalidade em 30 dias por insuficiência cardíaca são mostrados aqui.
# head(texas)
## Hospital.Name Rate Rank
## 3935 FORT DUNCAN MEDICAL CENTER 8.1 1
## 4085 TOMBALL REGIONAL MEDICAL CENTER 8.5 2
## 4103 CYPRESS FAIRBANKS MEDICAL CENTER 8.7 3
## 3954 DETAR HOSPITAL NAVARRO 8.7 4
## 4010 METHODIST HOSPITAL,THE 8.8 5
## 3962 MISSION REGIONAL MEDICAL CENTER 8.8 6Observe que o Cypress Fairbanks Medical Center e o Detar Hospital Navarro têm a mesma taxa de 30 dias (8,7). No entanto Cypress vem antes de Detar alfabeticamente, Cypress é classificado como número 3 neste esquema e Detar é classificado como número 4. Pode-se usar a função de ordem para classificar vários vetores desta maneira (ou seja, onde um vetor é usado para desempate em outro vetor). A função deve usar o seguinte modelo:
rankhospital <- function(state, outcome, num = "best")
{
## Ler dados de resultados.
## Verifique se o estado e o resultado são válidos.
## Devolva o nome do hospital nesse estado com a classificação dada taxa de mortalidade em 30 dias.
}A função deve verificar a validade de seus argumentos. Se o argumento
state for inválido a função deve gerar um erro por meio de
parada com a mensagem “invalid state”. Se o argumento ‘outcome’ for
inválido a função deve gerar um erro por meio de parada com a mensagem
“invalid outcome”.
3.8.2.3. Função rankall
Escreva uma função chamada rankall que receba dois
argumentos: o nome do resultado (outcome) e a classificação do hospital
(num). A função lê o arquivo outcome-of-care-measures.csv e
retorna um quadro de dados de 2 colunas contendo o hospital em cada
estado que possui a classificação especificada em ‘num’. Por exemplo, a
chamada de função rankall(“heart attack”, “best”) retornaria um quadro
de dados contendo os nomes dos hospitais que são os melhores em seus
respectivos estados para taxas de mortalidade por ataque cardíaco em 30
dias. A função deve retornar um valor para cada estado (alguns podem ser
NA). A primeira coluna no quadro de dados é denominada hospital, que
contém o nome do hospital, e a segunda coluna é denominada estado, que
contém a abreviação de 2 caracteres para o nome do estado. Hospitais que
não possuem dados sobre determinado desfecho devem ser excluídos do
conjunto de hospitais ao decidir os rankings. A função ‘rankall’ deve
lidar com empates nas taxas de mortalidade em 30 dias da mesma forma que
a função rankhospital lida com empates. A função deve usar
o seguinte modelo:
rankall <- function(outcome, num = "best")
{
## Ler dados de resultados.
## Verifique se o estado e o resultado são válidos.
## Para cada estado, encontre o hospital da classificação dada.
## Retorna um quadro de dados com os nomes dos hospitais e o nome do estado (abreviado).
}A função deve verificar a validade de seus argumentos. Se o argumento ‘outcome’ for inválido a função deve gerar um erro por meio de parada com a mensagem “invalid outcome”. A variável ‘num’ pode assumir valores “melhor”, “pior” ou um número inteiro indicando a classificação (números menores são melhores). Se o número dado por ‘num’ for maior que o número de hospitais naquele estado, então a função deve retornar NA.
4. Introdução à Probabilidade
4.1. A Ideia de Probabilidade
“A experiência não permite nunca atingir a certeza absoluta. Não devemos procurar obter mais que uma probabilidade.”
Historicamente, acredita-se os primeiros cálculos da teoria da probabilidade foram realizados por estudiosos italianos dos séculos XV e XVI, dentre os quais podemos destacar Frei Luca Pacioli (1445 - 1517), Tartaglia (1499 - 1557) e Girolamo Cardano (1501 - 1576). Frei Luca Pacioli, por exemplo, dedicou-se ao estudo do problema conhecido como o problema dos pontos (divisão de apostas), publicando, em 1494, na obra intitulada Summa de arithmetica, geometria, proportinoni e proportionalità, uma solução (incorreta) para este problema. Tal solução apontava que os jogadores deveriam dividir a aposta numa proporção de 5 por 3.
Tartaglia também trabalhou com o problema dos pontos. De acordo com Katz (2009), na obra intitulada General Trattato, publicada em 1556, este matemático afirmou que a solução apresentada por Pacioli para o problema dos pontos poderia estar incorreta. Já Girolamo Cardano, publicou em 1663 a obra Liber de Ludo Alae “[…] que buscava permitir a tomada de boas decisões nos problemas de jogos de azar encontrados naquela época”. De acordo com Todhunter (1965), a obra de Cardano pode ser entendida como um manual de jogos.
Embora os cálculos dessa teoria tenha começado com os italianos, o marco do início da teoria das probabilidades é considerado com a troca de correspondências entre os estudiosos franceses Blaise Pascal (1623 - 1662) e Pierre de Fermat (1601 - 1665). As cartas trocadas por estes estudiosos em 1654 apresentam discussões e uma solução de um problema semelhante ao problema dos pontos que foi apresentado a Pascal por Antoine Gombauld (1610 - 1685), um homem que ganhava a vida jogando e era conhecido como cavaleiro de Méré.
Atualmente, essa teoria deixou de ser um pequeno ramo da consolidada Ciência Matemática e se tornou uma ciência relacionada com fenômenos aleatórios, sendo a peça chave para a previsão e discussão desses fenômenos nas mais diversas áreas do conhecimento. Portanto, para iniciar nossos estudos desta teoria, vamos começar com um dos elementos mais importantes da teoria da probabilidade, o espaço amostral.
4.2. Espaço Amostral
Assim como o processo de amostragem nos gera uma amostra, o processo experimental nos gera resultados que, por analogia, são chamados de pontos amostrais. A partir dessa ideia, temos dois conceitos importantes: evento (que é uma coleção de pontos amostrais, e, em geral, é denotado por uma letra maiúscula), e espaço amostral (que é a coleção de todos os pontos amostrais, e é denotado por \(\Omega\)).
Para exemplificar esses conceitos utilizando o R, vamos considerar
que duas moedas são lançadas e suas faces são registradas. Quais
seriam todos os pontos amostrais que compõe o espaço amostral para este
experimento aleatório? Para responder essa questão, criemos o
experimento de lançar uma moeda de acordo com seguinte rotina em R
(chamada de tosscoin):
tosscoin <- function(times, makespace = FALSE)
{
temp <- list()
for (i in 1:times)
{
temp[[i]] <- c("H", "T")
}
res <- expand.grid(temp, KEEP.OUT.ATTRS = FALSE)
names(res) <- c(paste(rep("toss", times), 1:times, sep = ""))
if (makespace)
res$probs <- rep(1, 2^times)/2^times
return(res)
}
# Espaço Amostral: Lançar a moeda uma vez
tosscoin(2)## toss1 toss2
## 1 H H
## 2 T H
## 3 H T
## 4 T T
# Espaço Amostral: Lançar a moeda três vezes
tosscoin(3)## toss1 toss2 toss3
## 1 H H H
## 2 T H H
## 3 H T H
## 4 T T H
## 5 H H T
## 6 T H T
## 7 H T T
## 8 T T T
No entanto, em algumas situações, determinar o número de resultados que compreendem o espaço amostral (ou o evento) pode não ser uma tarefa muito simples, necessitando de alguns conceitos matemáticos mais elaborados como permutações, e combinações.
7.2.1. Permutações
As permutações como o próprio nome indica, consiste em permutar algo, isto é, formar agrupamentos ordenados com todos os elementos desse conjunto. De acordo com a literatura, existem dois tipos: as permutações simples, e as permutações de objetos similares. As permutações simples são, essencialmente, o número de permutações (troca de posições) de \(n\) objetos diferentes. Tal número é obtido, em toda sua simplicidade, pela equação \(P_n = n!\) (\(n\) fatorial). Por outro lado, em situações em que há repetição, trabalha-se com o conceito de permutação de objetos similares que é descrito, matematicamente, pela seguinte equação:
\[P_{n_r} = \dfrac{n!}{n_1!n_2! \ldots
n_r!}\] onde \(n_i, i = 1, 2, \ldots,
r\) são a contagem dos objetos similares (ou repetidos). No R,
para o primeiro caso (permutação simples), utilizamos a função
factorial:
# Uso da função factorial():
# factorial(x)
Pn <- factorial(10)
Pn## [1] 3628800
Para o segundo caso (permutação com objetos repetidos), podemos
trabalhar com o pacote gtools por meio da função
permutations com argumento
repeats.allowed = TRUE, isto é,
# Instalar e carregar os pacotes necessários
# install.packages('gtools')
library(gtools)## Warning: package 'gtools' was built under R version 4.2.2
##
## Attaching package: 'gtools'
## The following object is masked from 'package:psych':
##
## logit
# Espaço amostral: Urna com três bolas
x <- c('red', 'blue', 'black')
# Uso da função permutations()
# permutations(n, r, v = 1:n, set = TRUE, repeats.allowed = FALSE)
permutations(n = 3, r = 2, v = x, repeats.allowed = T)## [,1] [,2]
## [1,] "black" "black"
## [2,] "black" "blue"
## [3,] "black" "red"
## [4,] "blue" "black"
## [5,] "blue" "blue"
## [6,] "blue" "red"
## [7,] "red" "black"
## [8,] "red" "blue"
## [9,] "red" "red"
7.2.2. Combinações
Matematicamente, uma combinação simples é definida como a contagem de todos os subconjuntos com \(k\) elementos de um determinado conjunto, com \(k \leq n\). Ou seja,
\[C_{(n,r)}= \dfrac{n!}{(n-r)! \cdot
r!}\] No R, tais combinações são obtidas facilmente pela função
choose, isto é,
# Uso da função choose():
# choose(n, k)
Cnp <- choose(n = 24, k = 4)
Cnp## [1] 10626
4.3. O Conceito de Probabilidade
4.3.1. As Interpretações de Probabilidade
Atualmente, a teoria de probabilidade deixou de ser um pequeno ramo da consolidada Ciência Matemática e se tornou uma ciência relacionada com fenômenos aleatórios, sendo a peça chave para a previsão e discussão desses fenômenos nas mais diversas áreas do conhecimento. Na literatura, podemos encontrar três interpretações essenciais para o conceito de probabilidade: a interpretação frequentista, a interpretação clássica, e a interpretação subjetiva.
-
Intepretação Frequentista: Seja \(A\) um evento qualquer. Se \(n_A\) é o número de ocorrências de \(A\) em \(n\) repetições independentes do experimento, então a probabilidade de ocorrência de \(A\) é dada por:
\[P(A) = \lim_{n \to \infty}\dfrac{n_A}{n}\] -
Intepretação Clássica: Seja \(\Omega\) um espaço amostral e \(A\) um evento qualquer. Se \(N(\Omega)\) é o número de elementos do espaço amostral \(\Omega\) e \(N(A)\) é o número de elementos do evento \(A\), então a probabilidade de ocorrência do evento A é definida como:
\[P(A) = \dfrac{N(A)}{N(\Omega)}\]
Se um experimento tem como espaço amostral \(\Omega = \{e_1,e_2,\ldots,e_n\}\), com um número finito de elementos, dizemos que os eventos elementares \(\{e_i\}\) são equiprováveis, se todos tem a mesma probabilidade de ocorrer, isto é:
\[P(\{e_i\})=\frac{1}{n}\]
Desta forma, podemos definir a probabilidade de um evento \(E = \{e_{j1},\ldots,e_{jk}\}\), composto por \(k\) elementos (com \(k\) menor que \(n\)), como sendo:
\[P(E)=\frac{\text{número de casos favoráveis a E}}{\text{número de casos possíveis de $\Omega$}}=\frac{k}{n}\] - Intepretação Subjetiva: Essa visão surgiu no início da década de 1950, com Leonard J. Savage que deu um impulso considerável ao que é chamado de conceito subjetivo de probabilidade, esta sendo expressa como a informação de um indivíduo. Para Leonard J. Savage, essa visão sustenta que a probabilidade mede a confiança que um indivíduo tem na verdade de uma proposição. Em outras palavras, essa interpretação permite que a incerteza e a subjetividade intrínsecas do estudo sejam adicionadas nos processos de decisão.
4.3.2. A Definição Matemática de Probabilidade
Anteriormente, aprendemos formas de interpretar o que é uma probabilidade sob as óticas, objetiva e subjetiva, e também como atribuir a probabilidade sob essas óticas. Mas a nossa pergunta agora é: qual é a definição matemática de probabilidade? Para responder essa questão, nesta seção, iremos entender o que forma um espaço de probabilidade e como isso define formalmente a probabilidade. Para tal tarefa, os axiomas de Kolmogorov serão considerados uma vez que eles são responsáveis por garantir a existência da probabilidade.
Definição: Dizemos que uma função \(P\) definida em uma \(\sigma\)-álgebra \(\Lambda\) de subconjuntos de um espaço amostral \(\Omega\) e imagem restrita ao intervalo \([0,1]\), é uma probabilidade se satisfaz os seguintes axiomas de Kolmogorov:
- \(P(\Omega) = 1\).
- Para todo subconjunto \(A \in \Lambda\), \(0\leq P(\Omega) \leq 1\).
- Para toda sequência \(A_1, A_2, \ldots , A_n, \ldots \in \Lambda\), mutuamente exclusiva, tem-se que:
\[P\left(\bigcup_{i=1}^{\infty} A_i\right) = \sum_{i=1}^{\infty} P\left(A_i\right)\] Para exemplificar o cálculo de probabilidades no R, considere o experimento de retirar uma carta de um baralho padrão criado de acordo com a seguinte rotina em R:
# Rotina em R para o espaço amostral do baralho padrão (52 cartas)
cards <- function (jokers = FALSE, makespace = FALSE)
{
x <- c(2:10, "J", "Q", "K", "A")
y <- c("Club", "Diamond", "Heart", "Spade")
res <- expand.grid(rank = x, suit = y)
if (jokers)
{
levels(res$rank) <- c(levels(res$rank), "Joker")
res <- rbind(res, data.frame(rank = c("Joker", "Joker"),
suit = c(NA, NA)))
}
if (makespace)
{
res$probs <- rep(1, dim(res)[1])/dim(res)[1]
}
return(res)
}Denote o espaço de probabilidade associado ao experimento como S, e defina os subconjuntos A e B de acordo com a rotina em R:
# Espaço amostral do experimento
S <- cards(makespace = TRUE)
# Eventos A e B
A <- subset(S, suit == "Heart")
B <- subset(S, rank %in% 7:9)Para calcularmos a probabilidade de se tirar uma carta de copas (evento A) e a probabilidade de se tirar uma carta que seja um sete, oito ou nove (evento B) de acordo com o espaço amostral gerado pela rotina acima, com base na interpretação clássica e nos axiomas de Kolmogorov, vamos, inicialmente, criar uma função para o cálculo de probabilidades em R:
# Função para o cálculo de probabilidades
prob <- function (x, event = NULL, given = NULL, ...)
{
if (is.null(x$probs))
{
message("O espaço amostral, 'space', está sem a coluna das probabilidades, 'probs'")
stop("see ?probspace")
}
if (missing(event))
{
r <- TRUE
}
else
{
e <- substitute(event)
r <- eval(e, x, parent.frame())
if (!is.logical(r))
stop("'event' deve ser avaliado como lógico")
r <- r & !is.na(r)
if (!isTRUE(all.equal(sum(x$probs), 1)))
warning("'space' não tem probabilidade igual a 1.")
}
A <- x[r, ]
if (missing(given))
{
p <- sum(A$probs)
}
else
{
f <- substitute(given)
g <- eval(f, x, enclos = parent.frame())
if (!is.logical(g))
{
if (!is.data.frame(given))
stop("'given' deve ser um data.frame or avaliado com lógico")
B <- given
}
else
{
if (missing(event))
stop("'event' deve ser especificado quando 'given' é uma expressão")
g <- g & !is.na(g)
B <- x[g, ]
}
if (sum(B$probs <= 0))
stop("prob(given) deve ser positiva")
p <- sum(intersect(A, B)$probs)/sum(B$probs)
}
return(p)
}Com a função em mãos, calculamos, então, as probabilidades desejadas de acordo com a seguinte rotina em R:
# Probabilidade do evento A
prob(A)## [1] 0.25
# Probabilidade do evento B
prob(B)## [1] 0.2307692
4.4. Probabilidade Condicional
Com uma certa frequência, nos deparamos com a situação de um conhecimento adicional capaz de afetar a probabilidade do resultado de um dado experimento. Quando isso acontece, então, precisamos alterar a probabilidade de um evento de interesse. Essa nova probabilidade é conhecida como probabilidade condicional do evento e é um dos conceitos mais importantes de toda a teoria da probabilidade. Para obter tal probabilidade, procedemos da seguinte forma: dividimos a probabilidade da interseção dos eventos A e B, \(P(A \cap B)\), pela probabilidade total do espaço amostral, \(P(B)\). Ou seja, em linguagem matemática, a probabilidade de A dado que B ocorreu é descrita pela expressão:
\[P(A\mid B) = \dfrac{P(A\cap B)}{P(B)}\]
Para exemplificar, suponha que, em uma comunidade, 20% dos indivíduos adultos são hipertensos, 40% são diabéticos e 15% são hipertensos e diabéticos. Consideremos um experimento que consiste em selecionar ao acaso um indivíduo dessa comunidade. Defina os eventos:
D: o indivíduo escolhido é portador de diabetes.
H: o indivíduo escolhido é portador de hipertensão.
Note que, quando dizemos que 15% são hipertensos e diabéticos, nós estamos nos referindo a uma situação em que ambos os eventos, D e H, simultaneamente ocorrem. Nesse caso, nos referimos à interseção entre os eventos, denotada pelo símbolo \(\cap\). Evento resultante da interseção é, então:
- \(D \cap H\): o indivíduo escolhido é portador de diabetes e de hipertensão.
Assim, de acordo com a interpretação frequentista de probabilidade, ao selecionar ao acaso um indivíduo dessa comunidade, a probabilidade de ele ser portador de hipertensão é P(H) = 0,2. Porém, por outro lado, se partimos do conhecimento que o indivíduo selecionado é portador de diabetes, qual é agora a probabilidade de ele ser portador de hipertensão? Essa pergunta se refere a uma probabilidade condicional, denotada por P(H|D). Lemos o símbolo “|” como “dado que”. Assim, P(H|D) se refere à pro-babilidade de ocorrer o evento H, dado que o evento D ocorreu. Ou, no nosso exemplo, à probabilidade de o indivíduo selecionado ser portador de hipertensão, dado que ele é portador de diabetes.
No diagrama de Venn ilustrado na Figura 1, sombreamos a região associada ao evento D considerando que partimos do conhecimento de que esse evento ocorreu. Essa região sombreada corresponde a 40% dos indivíduos de toda a comunidade, e consideramos que o indivíduo selecionado pertence a essa parcela. Dentre esses 40%, sabemos que 15% são hipertensos. Portanto, a probabilidade de selecionarmos um indivíduo hipertenso entre aqueles que portam diabetes é 0,15/0,40 = 0,375.
Figura 1: Diagrama de Venn para uma comunidade em que 20% dos indivíduos adultos são hipertensos, 40% são diabéticos e 15% são hipertensos e diabéticos.
## Informações do problema
D <- 0.40
H <- 0.20
DH <- 0.15
## Carregar pacotes
library(VennDiagram)## Warning: package 'VennDiagram' was built under R version 4.2.2
## Loading required package: grid
## Loading required package: futile.logger
##
## Attaching package: 'futile.logger'
## The following object is masked from 'package:gtools':
##
## scat
library(tidyverse)
## Diagrama de Venn
grid.newpage()
invisible(draw.pairwise.venn(area1 = D, area2 = H, cross.area = DH,
category = c("D", "H"), lty = c("blank", "blank"),
fill = c("light blue", "pink"), alpha = c(0.5, 0.5),
cat.pos = c(0, 0), scaled = FALSE, height = 200, width = 200))Assim, para encontrarmos a probabilidade condicional P(H|D), trabalhamos com a definição dada anteriormente, isto é,
\[P(H\mid D) = \dfrac{P(D\cap H)}{P(D)}\]
desde que P(D) seja maior que zero. Se P(D) fosse igual a zero, não teríamos um único portador de diabetes na comunidade e não faria sentido buscar a probabilidade condicional P(H|D). No R, temos:
P_DH <- 0.15/0.40
P_DH## [1] 0.375
Como segundo exemplo, considere o experimento do lançamento de dois dados criado pela seguinte rotina em R:
# Rotina em R para o espaço amostral do lançamento de dois dados
rolldie <- function (times, nsides = 6, makespace = FALSE)
{
temp = list()
for (i in 1:times)
{
temp[[i]] <- 1:nsides
}
res <- expand.grid(temp, KEEP.OUT.ATTRS = FALSE)
names(res) <- c(paste(rep("X", times), 1:times, sep = ""))
if (makespace)
res$probs <- rep(1, nsides^times)/nsides^times
return(res)
}Denote o espaço de probabilidade associado ao experimento como S, e defina os subconjuntos A e B de acordo com a rotina em R:
# Espaço amostral do experimento
S <- rolldie(2, makespace = TRUE)
head(S)## X1 X2 probs
## 1 1 1 0.02777778
## 2 2 1 0.02777778
## 3 3 1 0.02777778
## 4 4 1 0.02777778
## 5 5 1 0.02777778
## 6 6 1 0.02777778
# Eventos A e B
A <- subset(S, X1 == X2)
A## X1 X2 probs
## 1 1 1 0.02777778
## 8 2 2 0.02777778
## 15 3 3 0.02777778
## 22 4 4 0.02777778
## 29 5 5 0.02777778
## 36 6 6 0.02777778
B <- subset(S, X1 + X2 >= 8)
B## X1 X2 probs
## 12 6 2 0.02777778
## 17 5 3 0.02777778
## 18 6 3 0.02777778
## 22 4 4 0.02777778
## 23 5 4 0.02777778
## 24 6 4 0.02777778
## 27 3 5 0.02777778
## 28 4 5 0.02777778
## 29 5 5 0.02777778
## 30 6 5 0.02777778
## 32 2 6 0.02777778
## 33 3 6 0.02777778
## 34 4 6 0.02777778
## 35 5 6 0.02777778
## 36 6 6 0.02777778
Com base nesses eventos, qual seria a probabilidade de ocorrência
que, no lançamento, ambos os dados tenham a mesma face (evento A) dado
que a soma das faces dos dados é maior ou igual a oito (evento B)? E
qual a probabilidade da soma das faces ser maior ou igual a oito, dado
que as faces dos dados são iguais? Para responder ambas as questões,
podemos trabalhar com a função prob() implementada
anteriormente, isto é,
# Probabilidade: P(A | B)
prob(A, given = B)## [1] 0.2
# Probabilidade: P(B | A)
prob(B, given = A)## [1] 0.5
4.5. Teorema de Bayes
Muitas vezes em nosso experimento, começamos a análise com estimativas de probabilidade iniciais (priori) ou anteriores para eventos específicos de interesse. Então, de fontes como uma amostra representativa, um relatório especial ou um teste de produto, obtemos informações adicionais sobre os eventos. Dadas essas novas informações, atualizamos os valores de probabilidade anteriores calculando as probabilidades revisadas, chamadas de probabilidades posteriores (posteriori). Este procedimento é conhecido como Teorema de Bayes. De modo geral, o Teorema de Bayes é aplicável quando os eventos para os quais queremos calcular probabilidades posteriores são mutuamente exclusivos e sua união é todo o espaço amostral, isto é, para o caso de \(n\) eventos mutuamente exclusivos \(A_1, A_2, \ldots, A_n\), cuja união é todo o espaço amostral, o Teorema de Bayes pode ser utilizado para calcular qualquer probabilidade posterior \(P(A_i \mid B)\) da seguinte forma:
\[P(A_i \mid B) = \dfrac{P(A_i) P(B \mid A_i)}{P(A_1) P(B \mid A_1)\ldots P(A_n) P(B \mid A_n)}\]
Para trabalhar com esse teorema no R, podemos fazer o uso da função
BayesTheorem disponível no pacote
LaplacesDemon. Essa função necessita, basicamente, de dois
argumentos: a probabilidade a priori do evento A, e a
probabilidade condicional do evento B dado o evento A
(também é conhecida como evidência). Para exemplificar,
vamos considerar o seguinte problema:
Dois tipos de vacina foram aplicados em uma população, de tal forma que 60% das pessoas receberam vacina do tipo A e as 40% restantes receberam vacina do tipo B. Sabendo que a vacina do tipo A fornece 70% de imunização, e a B fornece 80%, qual a probabilidade de que uma pessoa, escolhida ao acaso, tenha sido vacinada por A dado que ela não esteja imunizada?
Para responder essa questão, utilizamos a função
BayesTheorem, podemos calcular essa probabilidade pela
seguinte rotina em R:
## Carregar o pacote
library(LaplacesDemon)## Warning: package 'LaplacesDemon' was built under R version 4.2.2
##
## Attaching package: 'LaplacesDemon'
## The following objects are masked from 'package:gtools':
##
## ddirichlet, logit, rdirichlet
## The following object is masked from 'package:purrr':
##
## partial
## The following objects are masked from 'package:psych':
##
## logit, tr
## Determinar as probabilidades
PrA <- c(0.60, 0.40) # Probabilidade de ter sido vacidada por A (60%) e seu complemento
PrBA <- c(0.30, 0.20) # Probabilidade condicional referente a imunização de ambas as vacinas
## Utilizar o Teorema de Bayes
# Uso da função BayesTheorem():
# BayesTheorem(PrA, PrBA)
BayesTheorem(PrA, PrBA)[1] # [1] retorna a probabilidade, [2] retorna a probabilidade complementar## [1] 0.6923077
4.6. Distribuições de Probabilidade
4.6.1. Distribuição Binomial
Seja \(X\) uma variável aleatória discreta tal que \(X\) conta o número de tentativas que resultam em um sucesso em \(n\) tentativas. Neste caso, a distribuição de probabilidade de \(X\) é conhecida como distribuição Binomial com parâmetro \(\rho\) e função de probabilidade caracterizada por:
\[P(x) = \binom{n}{x} \rho^{x}(1-\rho)^{n-x}\]
em que \(0 < \rho < 1\). Note que se
\[X\sim Bin(n,\rho)\]
então,
\[E(X) = n\rho\]
\[Var(X) = n\rho(1-\rho)\] No R, a
distribuição binomial pode ser acessada pelas seguintes funções:
dbinom (função de probabilidade), pbinom
(função de distribuição acumulada), qbinom (função
quartil), e rbinom (função geradora de valores
aleatórios).
# Distribuição Binomial
# dbinom(x, size, prob, log = FALSE)
# pbinom(q, size, prob, lower.tail = TRUE, log.p = FALSE)
# qbinom(p, size, prob, lower.tail = TRUE, log.p = FALSE)
# rbinom(n, size, prob)
# Cálculo de probabilidade: Distribuição binomial
dbinom(x = 3, size = 10, prob = 0.5)## [1] 0.1171875
# Gráfico
plot(dbinom(1:80, size = 80, prob = 0.2), type = "h", lwd = 2,
main = "Distribuição de Probabilidade: Binomial",
ylab = "P(X = x)", xlab = "Número de Sucessos")4.6.2. Distribuição Geométrica
Seja \(X\) uma variável aleatória discreta tal que \(X\) conte o número de fracassos anteriores ao primeiro sucesso. Neste caso, a distribuição de probabilidade de \(X\) é conhecida como distribuição Geométrica com parâmetro \(\rho\) e tem função de probabilidade escrita na forma:
\[P(x) = \rho(1-\rho)^{x}, 0 < \rho < 1\]
Note que se
\[X\sim Geo(\rho)\]
então
\[E(X)=\dfrac{1-\rho}{\rho}\]
\[Var(X) = \dfrac{1- \rho}{\rho^2}\]
No R, a distribuição geométrica pode ser acessada pelas seguintes
funções: dgeom (função de probabilidade),
pgeom (função de distribuição acumulada),
qgeom (função quartil), e rgeom (função
geradora de valores aleatórios).
# Distribuição Goemétrica
# dgeom(x, prob, log = FALSE)
# pgeom(q, prob, lower.tail = TRUE, log.p = FALSE)
# qgeom(p, prob, lower.tail = TRUE, log.p = FALSE)
# rgeom(n, prob)
# Cálculo de probabilidade: Distribuição geométrica
dgeom(x = 2, prob = 0.5)## [1] 0.125
# Gráfico
plot(dgeom(1:80, prob = 0.2), type = "h", lwd = 2,
main = "Distribuição de Probabilidade: Geométrica",
ylab = "P(X = x)", xlab = "x")4.6.3. Distribuição de Poisson
Seja \(X\) uma variável aleatória discreta tal que \(X\) registre o número de eventos em um intervalo de tempo. A distribuição de probabilidade de \(X\) é conhecida como distribuição de Poisson com parâmetro \(\lambda\) e tem função de probabilidade escrita na forma:
\[P(x) = \dfrac{e^{-\lambda} \lambda^x}{x!}, \lambda > 0\]
Note que se
\[X \sim Poisson(\lambda)\]
então
\[E(X) = \lambda\] \[Var(X) = \lambda\]
No R, a distribuição de Poisson pode ser acessada pelas seguintes
funções: dpois (função de probabilidade),
ppois (função de distribuição acumulada),
qpois (função quartil), e rpois (função
geradora de valores aleatórios).
# Distribuição de Poisson
# dpois(x, lambda, log = FALSE)
# ppois(q, lambda, lower.tail = TRUE, log.p = FALSE)
# qpois(p, lambda, lower.tail = TRUE, log.p = FALSE)
# rpois(n, lambda)
# Cálculo de probabilidade: Distribuição de Poisson
dpois(x = 2, lambda = 0.5)## [1] 0.07581633
# Gráfico
plot(dpois(1:80, lambda = 40), type = "h", lwd = 2,
main = "Distribuição de Probabilidade: Poisson",
ylab = "P(X = x)", xlab = "x")4.6.4. Distribuição Uniforme
Dizemos que uma variável aleatória contínua \(X\) é distribuída uniformemente ao longo do intervalo \((a,b)\) se sua função densidade de probabilidade é dada por:
\[f_x(x) = \begin{cases} \dfrac{1}{b-a}, \quad \mbox{se } \mbox{ $a<x<b$} \\ 0, \hspace{1.33cm} \mbox{se } \mbox{ caso contrário} \end{cases}\]
Nesse caso, se
\[X \sim U(a,b)\]
então
\[E(X) = \dfrac{b+a}{2}\] \[Var(X) = \dfrac{(b-a)^2}{12}\]
No R, a distribuição Uniforme pode ser acessada pelas seguintes
funções: dunif (função de probabilidade),
punif (função de distribuição acumulada),
qunif (função quartil), e runif (função
geradora de valores aleatórios).
# Distribuição Uniforme
# dunif(x, min = 0, max = 1, log = FALSE)
# punif(q, min = 0, max = 1, lower.tail = TRUE, log.p = FALSE)
# qunif(p, min = 0, max = 1, lower.tail = TRUE, log.p = FALSE)
# runif(n, min = 0, max = 1)
# Cálculo de probabilidade: Distribuição Uniforme
dunif(x = 2, min = 0, max = 4)## [1] 0.25
# Gráfico
plot(dunif(seq(0, 10, by = 0.1), min = 0, max = 100), type = "l", lwd = 2,
main = "Distribuição de Probabilidade: Uniforme",
ylab = "f(x)", xlab = "x")4.6.5. Distribuição Weibull
Dizemos que uma variável aleatória contínua \(X\) segue uma distribuição Weibull com parâmetros \(\lambda >0\) e \(\alpha >0\) se sua função densidade de probabilidade é dada por:
\[f_x(x) = \dfrac{\alpha}{\lambda^{\alpha}} x^{\alpha -1} e^{\left[-(\frac{x}{\lambda})^{\alpha}\right]}, x > 0\]
Note que se \[X \sim Weibull(\lambda,\alpha)\]
então
\[E(X) = \lambda \Gamma(1+\alpha^{-1})\] \[Var(X) = \lambda^2[\Gamma(1+2\alpha^{-1}) - \Gamma(1+\alpha^{-1})]\]
No R, a distribuição Weibull pode ser acessada pelas seguintes
funções: dweibull (função de probabilidade),
pweibull (função de distribuição acumulada),
qweibull (função quartil), e rweibull (função
geradora de valores aleatórios).
# Distribuição Weibull
# dweibull(x, shape, scale = 1, log = FALSE)
# pweibull(q, shape, scale = 1, lower.tail = TRUE, log.p = FALSE)
# qweibull(p, shape, scale = 1, lower.tail = TRUE, log.p = FALSE)
# rweibull(n, shape, scale = 1)
# Cálculo de probabilidade: Distribuição Weibull
dweibull(x = 1.8, shape = 2, scale = 0.4)## [1] 3.611763e-08
# Gráfico
plot(seq(0, 1, by = 0.01), dweibull(seq(0, 1, by = 0.01), shape = 0.8, scale = 0.4), type = "l", lwd = 2,
main = "Distribuição de Probabilidade: Weibull",
ylab = "f(x)", xlab = "x")4.6.6. Distribuição Qui-Quadrado
Uma variável aleatória contínua \(X\) segue uma distribuição qui-quadrado com \(\nu\) graus de liberdade se sua função densidade for escrita na forma:
\[f_x(x)=\frac{1}{2^{\nu/2}\Gamma(\nu/2)}x^{(v/2)-1}e^{(-\frac{x}{2})}; \nu > 0, x > 0\]
sendo \(\Gamma(\omega)=\int_0^{\infty}x^{\omega-1}e^{-x}dx,
\omega > 0\). No R, a distribuição Qui-Quadrado pode ser
acessada pelas seguintes funções: dchisq (função de
probabilidade), pchisq (função de distribuição acumulada),
qchisq (função quartil), e rchisq (função
geradora de valores aleatórios).
# Distribuição Qui-Quadrado
# dchisq(x, df, ncp = 0, log = FALSE)
# pchisq(q, df, ncp = 0, lower.tail = TRUE, log.p = FALSE)
# qchisq(p, df, ncp = 0, lower.tail = TRUE, log.p = FALSE)
# rchisq(n, df, ncp = 0)
# Cálculo de probabilidade: Distribuição Qui-Quadrado
dchisq(x = 1.8, df = 5)## [1] 0.1305667
# Gráfico
plot(seq(0, 20, by = 0.1), dchisq(seq(0, 20, by = 0.1), df = 3), type = "l", lwd = 2,
main = "Distribuição de Probabilidade: Qui-Quadrado",
ylab = "f(x)", xlab = "x")4.6.7. Distribuição t de Student
Uma variável aleatória contínua \(X\) tem distribuição \(t\) de Student com \(\nu\) graus de liberdade se sua função densidade de probabilidade é dada por:
\[f_x(x)=\frac{\Gamma\left(\frac{\nu+1}{2}\right)}{\sqrt{\nu\pi}\Gamma\left(\frac{\nu}{2}\right)}\left(1+\frac{x^2}{\nu}\right)^{-\left(\frac{\nu+1}{2}\right)},\qquad
x \in(-\infty,\infty)\] No R, a distribuição t de Student pode
ser acessada pelas seguintes funções: dt (função de
probabilidade), pt (função de distribuição acumulada),
qt (função quartil), e rt (função geradora de
valores aleatórios).
# Distribuição t de Student
# dt(x, df, ncp, log = FALSE)
# pt(q, df, ncp, lower.tail = TRUE, log.p = FALSE)
# qt(p, df, ncp, lower.tail = TRUE, log.p = FALSE)
# rt(n, df, ncp)
# Cálculo de probabilidade: Distribuição t de Student
dt(x = 1.8, df = 5)## [1] 0.08481296
# Gráfico
plot(seq(-5, 5, by = 0.1), dt(seq(-5, 5, by = 0.1), df = 5), type = "l", lwd = 2,
main = "Distribuição de Probabilidade: t de Student",
ylab = "f(x)", xlab = "x")4.6.8. Distribuição Normal
Uma variável aleatória contínua \(X\) tem distribuição normal com parâmetros \(-\infty < \mu < \infty\) e \(\sigma^2 >0\) se sua função densidade de probabilidade for dada por:
\[f_x(x)=\frac{1}{\sqrt{2\pi\sigma^2}}e^{-\frac{1}{2}\left(\frac{x-\mu}{\sigma}\right)^2}, \quad x\in(-\infty,\infty)\]
Assim, se
\[X\sim N(\mu,\sigma^2)\]
então
\[E(X) = \mu\] \[Var(X) = \sigma^2\]
Além disso, \(f_x(x)\) é simétrica em relação à \(\mu\) e, quando \(\mu=0\) e \(\sigma^2 = 1\), sua densidade se reduz a:
\[f(x)=\frac{1}{\sqrt{2\pi}}e^{-\frac{1}{2}x^2}\]
e é conhecida como distribuição Normal padrão. Neste caso, dizemos
que \(X \sim N(0,1)\). No R, a
distribuição normal pode ser acessada pelas seguintes funções:
dnorm (função de probabilidade), pnorm (função
de distribuição acumulada), qnorm (função quartil), e
rnorm (função geradora de valores aleatórios).
# Distribuição Normal
# dnorm(x, mean = 0, sd = 1, log = FALSE)
# pnorm(q, mean = 0, sd = 1, lower.tail = TRUE, log.p = FALSE)
# qnorm(p, mean = 0, sd = 1, lower.tail = TRUE, log.p = FALSE)
# rnorm(n, mean = 0, sd = 1)
# Cálculo de probabilidade: Distribuição Normal
dnorm(x = 1.8, mean = 5, sd = 1)## [1] 0.002384088
# Gráfico
plot(seq(0, 20, by = 0.1), dnorm(seq(0, 20, by = 0.1), mean = 10, sd = 3), type = "l", lwd = 2,
main = "Distribuição de Probabilidade: Normal",
ylab = "f(x)", xlab = "x")4.7. Teorema do Limite Central
Na teoria da probabilidade, o Teorema do Limite Central (TLC) estabelece que, em muitas situações, quando variáveis aleatórias independentes são somadas, sua soma devidamente normalizada tende a uma distribuição normal mesmo que as variáveis originais não sejam normais. O teorema é um conceito-chave na teoria da probabilidade porque implica que métodos probabilísticos e estatísticos que funcionam para distribuições normais podem ser aplicáveis a muitos problemas envolvendo outros tipos de distribuições. Este teorema sofreu muitas mudanças durante o desenvolvimento formal da teoria da probabilidade. As versões anteriores do teorema datam de 1811, mas em sua forma geral moderna, esse resultado fundamental na teoria da probabilidade foi declarado com precisão até 1920, servindo assim como uma ponte entre a teoria clássica e a teoria moderna da probabilidade. Para explificar o uso do teorema, consideraremos algumas distribuições clássicas de probabilidade.
4.7.1. Distribuição Uniforme
Considere a seguinte amostra gerada de uma distribuição uniforme:
unif <- c(1:8)
unif## [1] 1 2 3 4 5 6 7 8
Com base nos conceitos de Estatística Descritiva, obtemos que a média e o desvio-padrão amostrais são descritos por:
mean(unif)## [1] 4.5
sd(unif)## [1] 2.44949
Graficamente, temos:
hist(unif, main = "Distribuição Uniforme", xlab = " ")Para entender o teorema central do limite, faremos um laço de
repetição do tipo for para continuar calculando a média
amostral de cada amostra coletada. Para este processo, recomenda-se
utilizar um número alto de repetições e, também, definir uma semente
(pelo comando set.seed()) para garantir que o mesmo
resultado seja obtido quando qualquer usuário iniciar com a mesma
semente toda vez que o mesmo processo for executado. Além disso, devemos
iniciar o processo a partir de um determinado tamanho amostral e ir
aumentando até for possível garantir a convergência.
set.seed(123)
par(mfrow = c(2,3))
# Tamanho de Amostra 3
sample_means <- c()
for(i in 1:1000)
{
sample_means[i] <- mean(sample(8, 3, replace = TRUE))
}
hist(sample_means, xlim = c(0,8), main = "Tamanho de Amostra 3", xlab = "Médias Amostrais")
# Tamanho de Amostra 10
for(i in 1:1000)
{
sample_means[i] <- mean(sample(8, 10, replace = TRUE))
}
hist(sample_means, xlim = c(0,8), main = "Tamanho de Amostra 10", xlab = "Médias Amostrais")
# Tamanho de Amostra 30
for(i in 1:1000)
{
sample_means[i] <- mean(sample(8, 30, replace = TRUE))
}
hist(sample_means, xlim = c(0,8), main = "Tamanho de Amostra 30", xlab = "Médias Amostrais")
# Tamanho de Amostra 50
for(i in 1:1000)
{
sample_means[i] <- mean(sample(8, 50, replace = TRUE))
}
hist(sample_means, xlim = c(0,8), main = "Tamanho de Amostra 50", xlab = "Médias Amostrais")
# Tamanho de Amostra 75
for(i in 1:1000)
{
sample_means[i] <- mean(sample(8, 75, replace = TRUE))
}
hist(sample_means, xlim = c(0,8), main = "Tamanho de Amostra 75", xlab = "Médias Amostrais")
# Tamanho de Amostra 100
for(i in 1:1000)
{
sample_means[i] <- mean(sample(8, 100, replace = TRUE))
}
hist(sample_means, xlim = c(0,8), main = "Tamanho de Amostra 100", xlab = "Médias Amostrais")4.7.2. Distribuição Binomial
Vamos dar uma olhada na distribuição binomial para avaliar o TCL.
Neste caso, Para criar a distribuição de probabilidade binomial,
usaremos a função dbinom(x, size, prob) onde x
= vetor do número de sucessos, size = tamanho da amostra,
prob = probabilidade de sucesso. Já para a representação
gráfica, usaremos a função plot(x, y, type = "h") onde x =
vetor do número de sucessos, y = dbinom() e type = “h” para histograma
como linhas verticais.
par(mfrow = c(2,3))
# Tamanho de Amostra 10
success <- c(0:10)
plot(success, dbinom(success, size = 10, prob = 0.25),
type = "h",
main = "Tamanho de Amostra 10, p = 0.25",
xlab = "Número de Sucessos",
ylab = "Probabilidade de Sucesso",
lwd = 3)
# Tamanho de Amostra 30
suc <- c(0:30)
y <- dbinom(suc, size = 30, prob = 0.25)
plot(suc, y,
type = "h",
main = "Tamanho de Amostra 30, p = 0.25",
xlab = "Número de Sucessos",
ylab = "Probabilidade de Sucesso")
# Tamanho de Amostra 100
suc <- c(0:100)
y <- dbinom(suc, size = 100, prob = 0.25)
plot(suc, y,
type = "h",
main = "Tamanho de Amostra 100, p = 0.25",
xlab = "Número de Sucessos",
ylab = "Probabilidade de Sucesso")
# Tamanho de Amostra 200
suc <- c(0:200)
y <- dbinom(suc, size = 200, prob = 0.25)
plot(suc, y,
type = "h",
main = "Tamanho de Amostra 200, p = 0.25",
xlab = "Número de Sucessos",
ylab = "Probabilidade de Sucesso")
# Tamanho de Amostra 300
suc <- c(0:300)
y <- dbinom(suc, size = 300, prob = 0.25)
plot(suc, y,
type = "h",
main = "Tamanho de Amostra 300, p = 0.25",
xlab = "Número de Sucessos",
ylab = "Probabilidade de Sucesso")
# Tamanho de Amostra 1000
suc <- c(0:1000)
y <- dbinom(suc, size = 1000, prob = 0.25)
plot(suc, y,
type = "h",
main = "Tamanho de Amostra 1000, p = 0.25",
xlab = "Número de Sucessos",
ylab = "Probabilidade de Sucesso")4.7.3. Distribuição Weibull
Vamos dar uma olhada agora na distribuição Weibull para avaliar o
TCL. Neste caso, Para criar a distribuição de probabilidade Weibull,
usaremos a função dweibull(x, shape, scale) onde x = vetor
de valores, shape = parâmetro de forma, scale = parâmetro de escala.
set.seed(123)
par(mfrow = c(2,3))
# Tamanho de Amostra 3
sample_means <- c()
for(i in 1:1000)
{
sample_means[i] <- mean(rweibull(n = 3, shape = 2, scale = 2))
}
hist(sample_means, main = "Tamanho de Amostra 3", xlab = "Médias Amostrais")
# Tamanho de Amostra 10
for(i in 1:1000)
{
sample_means[i] <- mean(rweibull(n = 10, shape = 2, scale = 2))
}
hist(sample_means, main = "Tamanho de Amostra 10", xlab = "Médias Amostrais")
# Tamanho de Amostra 30
for(i in 1:1000)
{
sample_means[i] <- mean(rweibull(n = 30, shape = 2, scale = 2))
}
hist(sample_means, main = "Tamanho de Amostra 30", xlab = "Médias Amostrais")
# Tamanho de Amostra 50
for(i in 1:1000)
{
sample_means[i] <- mean(rweibull(n = 50, shape = 2, scale = 2))
}
hist(sample_means, main = "Tamanho de Amostra 50", xlab = "Médias Amostrais")
# Tamanho de Amostra 75
for(i in 1:1000)
{
sample_means[i] <- mean(rweibull(n = 75, shape = 2, scale = 2))
}
hist(sample_means, main = "Tamanho de Amostra 75", xlab = "Médias Amostrais")
# Tamanho de Amostra 100
for(i in 1:1000)
{
sample_means[i] <- mean(rweibull(n = 100, shape = 2, scale = 2))
}
hist(sample_means, main = "Tamanho de Amostra 100", xlab = "Médias Amostrais")5. Teste de Hipóteses
Nosso foco, agora, é entender as principais ferramentas para o auxilio de tomada de decisões sobre um grande volume de dados que definem a Inferência Estatística. As primeiras ferramentas do processo de Inferência Estatística são caracterizadas pelos modelos de probabilidade que já vimos anteriormente. As outras ferramentas envolvem as distribuições amostrais e os testes de hipóteses (TH) que serão nosso foco aqui.
A nossa primeira questão sobre os testes de hipóteses é: o que é um teste de hipóteses? Por definição, um teste de hipóteses é uma regra de decisão que permitem rejeitar ou não as hipóteses testadas, com um determinado nível de confiança, baseados em valores amostrais. Neste contexto, então, começa-se fazendo uma suposição provisória sobre um parâmetro populacional. Essa suposição provisória é chamada de hipótese nula e é denotada por \(H_0\). Em seguida, definimos outra hipótese, chamada de hipótese alternativa, que é o oposto do que é afirmado na hipótese nula. Tal hipótese é denotada por \(H_a\).
Nem sempre é óbvio como as hipóteses nula e alternativa devem ser formuladas. É preciso tomar cuidado para estruturar as hipóteses apropriadamente, de modo que a conclusão do teste de hipótese forneça as informações que o pesquisador ou tomador de decisão deseja. Naturalmente, o contexto da situação é muito importante para determinar como as hipóteses devem ser enunciadas. Todas as aplicações de teste de hipótese envolvem coletar uma amostra e usar os resultados amostrais a fim de fornecer evidências para chegar a uma conclusão. Alguns exemplos de boas questões a serem consideradas ao se formular as hipóteses nula e alternativa são: (1) Qual é o objetivo pretendido ao se coletar a amostra? (2) A quais conclusões esperamos chegar? (3) Quais são os parâmetros de interesse?
5.1. Tipos de Erros
Por outro lado, é válido destacar que, em todo teste de hipótese, as hipóteses, nula e alternativa, são afirmações excludentes sobre a população, ou seja, ou a hipótese nula é verdadeira ou a hipótese alternativa é verdadeira, mas nunca ambas ao mesmo tempo. Em geral, espera-se que o teste de hipóteses leve à não-rejeição da hipótese nula quando ela for verdadeira e à rejeição de dela quando ela for falsa. No entanto, tais conclusões nem sempre são possíveis, e isso nos gera dois tipos de erros: erro do tipo I, e o erro do tipo II.
O erro do tipo I é o erro que ocorre quando rejeitamos a hipótese nula e ela é verdadeira, isto é, rejeitar a hipótese nula, neste caso, não é uma decisão correta. Esse tipo de erro é denotado por \(\alpha\) e é conhecido como nível de significância do teste de hipóteses. Já o erro do tipo II é o erro que ocorre quando não-rejeitamos a hipótese nula quando ela é falsa, isto é, descartamos a hipótese alternativa que é verdadeira, também não é uma decisão correta. Esse é denotado por \(\beta\) e não pode ser controlado.
|
Condição da População
|
|||
|---|---|---|---|
| H0 Verdadeira | H0 Falsa | ||
| Conclusão | Não Rejeita H0 | Conclusão Correta | Erro do Tipo II |
| Rejeita H0 | Erro do Tipo I | Conclusão Correta | |
5.2. Tipos de Hipóteses
Na prática, existem duas formas de realizarmos um teste de hipóteses. Se nosso interesse for em avaliar se há uma diferença em relação ao valor definido para a \(H_0\), tanto positiva como negativa, o teste será chamado de bilateral e suas hipóteses serão definidas como:
\[H_0: \text{Parâmetro Populacional = Parâmetro de Teste}\] \[vs.\] \[H_a: \text{Parâmetro Populacional ≠ Parâmetro de Teste}\]
Às vezes, porém, pode-se supor que uma diferença real possa ocorrer somente em um sentido, de tal forma que, se ocorrer uma diferença no outro sentido, isso é devido ao acaso. Nesse caso, o teste será chamado de unilateral e suas hipóteses serão definidas como:
\[H_0: \text{Parâmetro Populacional } \leq \text{(ou } \geq \text{) Parâmetro de Teste}\] \[vs.\] \[H_a: \text{Parâmetro Populacional } > \text{(ou } < \text{) Parâmetro de Teste}\] Agora que já sabemos o que é um teste de hipóteses, os tipos de erros e os tipos de hipóteses, nos resta saber como realizar um teste de hipóteses. Para tal tarefa, podemos trabalhar com as seguintes etapas:
- Etapa 1: Declarar as hipóteses nula e alternativa de acordo com o objetivo do experimento.
- Etapa 2: Definir o nível de significância do teste (erro do tipo I)
- Etapa 3: Definir a estatística do teste que será utilizada de acordo com distribuição amostral de interesse para o teste.
- Etapa 4: Definir a região crítica do teste de hipóteses que são os valores numéricos da estatística do teste para os quais a hipótese nula será rejeitada.
- Etapa 5: Calcular a estatística do teste definida na Etapa 3.
- Etapa 6: Fazer a decisão estatística do teste, isto é, rejeitar ou não \(H_0\) de acordo com a região crítica definida na Etapa 4 e no valor da estatística do teste.
- Etapa 7: Conclusão prática do teste, isto é, o que o resultado obtido na Etapa 6 diz na prática sobre o experimento.
5.3. Teste de Média
O nosso primeiro teste paramétrico a ser estudo é o teste de hipóteses para média de uma amostra. Este teste, em geral, é utilizado para comprovar se determinada população possui ou não o valor da média definida na hipótese nula. Para a construção deste teste, há duas possibilidades: amostras grandes, e amostras pequenas. No primeiro caso, o nosso teste será baseado na estatística Z (Teste Z), e no segundo caso, o nosso teste será baseado na estatística T (Teste T).
| Amostras Grandes | Teste Unilateral (Superior) | Teste Unilateral (Inferior) | Teste Bilateral |
|---|---|---|---|
| Hipótese | \(H_0: \mu \geq
\mu_0\) \(H_a: \mu < \mu_0\) |
\(H_0: \mu \leq
\mu_0\) \(H_a: \mu > \mu_0\) |
\(H_0: \mu = \mu_0\)
\(H_a: \mu \neq \mu_0\) |
| Estatística do Teste | \(z_{calc} = \dfrac{\bar{x} - \mu_0}{\sigma/\sqrt{n}}\) | ||
| Região Crítica | Rejeita-se \(H_0\) se \(z_{calc} \leq -z_{1-\alpha}\) | Rejeita-se \(H_0\) se \(z_{calc} \geq z_{1-\alpha}\) | Rejeita-se \(H_0\) se
\(z_{calc} \leq -z_{1-\alpha/2}\)
ou se \(z_{calc} \geq z_{1-\alpha/2}\) |
Para trabalhar com o Teste Z no R, fazemos o uso da
função z.test do pacote BSDA que é uma função
baseada na distribuição normal padrão com objetivo de criar intervalos
de confiança e testes de hipóteses para problemas com uma ou duas
amostras. Para exemplificar, vamos considerar a seguinte situação:
Sabe-se que a infecção por E. canis é
uma doença de cães transmitida por carrapatos que às vezes é contraída
por humanos. Entre os humanos infectados, a distribuição da contagem de
glóbulos brancos tem uma média desconhecida \(\mu\) e um desvio padrão \(\sigma\). Dado que na população geral a
contagem média de glóbulos brancos é de 7250/mm³, um pesquisador clínico
elaborou a hipótese de que as pessoas infectadas com E. canis devem ter,
em média, contagens mais baixas de glóbulos brancos do que a população
geral.
Perguntas de Interesse:
Quais seriam as hipóteses nula
e alternativa para o teste de hipóteses com o objetivo de dar suporte a
hipótese elaborada pelo pesquisador clínico? Essas hipóteses formam um
teste de hipóteses bilateral ou um teste de hipóteses
unilateral?
Resposta: Nesse caso, o teste será unilateral e suas hipóteses serão definidas como:
\[H_0: \mu < 7250/mm³\] \[vs.\] \[H_a: \mu \geq 7250/mm³\] Sabendo que, para uma amostra de 45 infectados (vetor \(x\) na rotina em R abaixo) com E. canis, a média de glóbulos brancos foi, aproximadamente, 7013/mm³, e baseando-se em experiências anteriores, obteve-se um desvio padrão populacional \(\sigma\) = 3204/mm³, qual é a conclusão do teste de hipóteses ao nível de significância de 5%?
## Carregar o pacote
library(BSDA)## Warning: package 'BSDA' was built under R version 4.2.2
## Loading required package: lattice
##
## Attaching package: 'lattice'
## The following object is masked from 'package:epiDisplay':
##
## dotplot
##
## Attaching package: 'BSDA'
## The following object is masked from 'package:datasets':
##
## Orange
## Carregar os dados:
x <- c(6888, 6954, 7312, 7014, 7026, 7343, 7092, 6747, 6863, 6911, 7245, 7072,
7080, 7022, 6889, 7357, 7100, 6607, 7140, 6905, 6786, 6956, 6795, 6854,
6875, 6663, 7168, 7031, 6772, 7251, 7085, 6941, 7179, 7176, 7164, 7138,
7111, 6988, 6939, 6924, 6861, 6958, 6747, 7434, 7242)
## Tamanho da amostra:
length(x)## [1] 45
## Como n > 30, fazer o uso do teste Z pela função z.test
# Uso da função z.test():
# z.test(x, y = NULL, alternative = "two.sided", mu = 0, sigma.x = NULL, sigma.y = NULL, conf.level = 0.95)
z.test(x = x, alternative = 'greater', mu = 7250, conf.level = 0.95, sigma.x = 3204)##
## One-sample z-Test
##
## data: x
## z = -0.49528, p-value = 0.6898
## alternative hypothesis: true mean is greater than 7250
## 95 percent confidence interval:
## 6227.823 NA
## sample estimates:
## mean of x
## 7013.444
## Valor de Z tabelado para hipótese do tipo menor:
qnorm(0.05)## [1] -1.644854
## Conclusão:
# Como o valor calculado para Z, -0.4953, é maior do que o valor tabelado de Z, -1.65, não rejeita-se H0.
# Isto é, ao nível de significância de 5%, não há evidências suficientes para dizer que a média de
# glóbulos brancos de pacientes infectados com E. canis é superior a da população geral, de 7250/mm³.| Amostras Pequenas | Teste Unilateral (Superior) | Teste Unilateral (Inferior) | Teste Bilateral |
|---|---|---|---|
| Hipótese | \(H_0: \mu \geq
\mu_0\) \(H_a: \mu < \mu_0\) |
\(H_0: \mu \leq
\mu_0\) \(H_a: \mu > \mu_0\) |
\(H_0: \mu = \mu_0\)
\(H_a: \mu \neq \mu_0\) |
| Estatística do Teste | \(t_{calc} = \dfrac{\bar{x} - \mu_0}{s/\sqrt{n}}\) | ||
| Região Crítica | Rejeita-se \(H_0\) se \(t_{calc} \leq -t_{\alpha}\) | Rejeita-se \(H_0\) se \(t_{calc} \geq t_{\alpha}\) | Rejeita-se \(H_0\) se
\(t_{calc} \leq -t_{\alpha/2}\) ou se \(t_{calc} \geq t_{\alpha/2}\) |
Para trabalhar com o Teste t no R, fazemos o uso da
função t.test disponível no R que é uma função
baseada na distribuição t de Student com objetivo de criar intervalos de
confiança e testes de hipóteses para problemas com uma ou duas amostras
quando o desvio-padrão populacional é desconhecido. Para exemplificar,
vamos considerar a seguinte situação:
A insuficiência cardíaca é uma síndrome clínica caracterizada pela incapacidade do coração de atuar adequadamente como bomba, quer seja por déficit de contração e/ou de relaxamento, comprometendo o funcionamento do organismo. Neste contexto, Ahmad (2017) realizou um estudo sobre os fatores que associados a insuficiência cardíaca de pacientes admitidos no Instituto de Cardiologia do Paquistão no período de Abril a Dezembro de 2015. Os dados do estudo são fornecidos na Tabela 1.
| Time | Event | Gender | Smoking | Diabetes | BP | Anaemia | Age | Ejection.Fraction | Sodium | Creatinine | Pletelets | CPK | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 261 | 109 | Censored | Female | No | Yes | No | No | 50.000 | 30 | 132 | 0.90 | 329000 | 482 |
| 193 | 207 | Non-Censored | Female | No | No | No | No | 60.000 | 30 | 127 | 1.70 | 62000 | 166 |
| 32 | 250 | Censored | Male | No | No | No | No | 60.000 | 35 | 140 | 1.70 | 279000 | 253 |
| 29 | 30 | Non-Censored | Male | No | Yes | No | No | 69.000 | 35 | 134 | 3.50 | 228000 | 582 |
| 257 | 67 | Non-Censored | Male | No | Yes | No | No | 65.000 | 25 | 135 | 1.83 | 497000 | 113 |
| 144 | 205 | Censored | Male | Yes | No | No | Yes | 58.000 | 25 | 132 | 1.30 | 189000 | 57 |
| 170 | 285 | Censored | Male | Yes | No | No | No | 50.000 | 45 | 136 | 1.60 | 395000 | 196 |
| 134 | 186 | Censored | Male | No | No | Yes | No | 70.000 | 60 | 138 | 0.90 | 220000 | 97 |
| 228 | 33 | Non-Censored | Female | No | Yes | No | Yes | 60.000 | 60 | 142 | 1.10 | 194000 | 588 |
| 226 | 72 | Censored | Male | Yes | Yes | No | No | 65.000 | 50 | 137 | 1.30 | 149000 | 224 |
| 277 | 147 | Censored | Male | Yes | No | No | No | 55.000 | 40 | 140 | 0.70 | 279000 | 835 |
| 52 | 28 | Non-Censored | Female | No | Yes | Yes | Yes | 50.000 | 35 | 128 | 1.00 | 319000 | 249 |
| 208 | 87 | Censored | Female | No | No | Yes | No | 60.000 | 50 | 143 | 2.30 | 286000 | 53 |
| 233 | 193 | Non-Censored | Female | No | Yes | Yes | Yes | 48.000 | 30 | 130 | 1.60 | 244000 | 131 |
| 71 | 172 | Non-Censored | Female | No | No | No | No | 50.000 | 50 | 134 | 0.60 | 153000 | 582 |
| 114 | 194 | Censored | Male | No | No | Yes | Yes | 65.000 | 35 | 134 | 0.80 | 290000 | 135 |
| 142 | 230 | Censored | Male | No | Yes | No | Yes | 45.000 | 25 | 135 | 0.80 | 233000 | 66 |
| 136 | 171 | Non-Censored | Male | No | Yes | No | Yes | 60.667 | 30 | 136 | 1.50 | 389000 | 104 |
| 108 | 121 | Censored | Male | No | No | No | Yes | 85.000 | 50 | 134 | 1.30 | 235000 | 910 |
| 218 | 45 | Non-Censored | Female | Yes | Yes | No | Yes | 60.000 | 38 | 132 | 2.20 | 255000 | 260 |
Perguntas de Interesse:
Um pesquisador clínico sugere que a média dos valores de CPK dos pacientes com insuficiência cardíaca é superior a 450. Suponha que o interesse seja testar essa hipótese, quais seriam as hipóteses apropriadas para o teste, neste caso?
Resposta: Nesse caso, o teste será unilateral e suas hipóteses serão definidas como:
\[H_0: \mu > 450\] \[vs.\] \[H_a: \mu \leq 450\]
Considerando a amostra descrita na Tabela 1, quais seriam os valores da média e desvio-padrão amostral? Baseando-se nestes resultados, a amostra fornecida é suficiente para apoiar a hipótese do pesquisador, considerando um nível de significância de 5%?
## Carregar os dados:
x <- db.heart$CPK
x## [1] 482 166 253 582 113 57 196 97 588 224 835 249 53 131 582 135 66 104 910
## [20] 260
## Tamanho da amostra:
length(x)## [1] 20
## Como n < 30, fazer o uso do teste T pela função t.test
# Uso da função t.test():
# t.test(x, y = NULL, alternative = c("two.sided", "less", "greater"), mu = 0,
# paired = FALSE, var.equal = FALSE, conf.level = 0.95, ...)
t.test(x = x, alternative = 'less', mu = 450, conf.level = 0.95)##
## One Sample t-test
##
## data: x
## t = -2.4753, df = 19, p-value = 0.01145
## alternative hypothesis: true mean is less than 450
## 95 percent confidence interval:
## -Inf 406.0326
## sample estimates:
## mean of x
## 304.15
## Valor de T tabelado para hipótese do tipo maior:
qt(0.05, 19)## [1] -1.729133
## Conclusão:
# Como o valor calculado para T, -2.4753, é menor do que o valor tabelado de T, -1.73, rejeita-se H0.
# Isto é, ao nível de significância de 5%, não há evidências suficientes para apoiar a hipótese do
# pesquisador clínico a qual sugere que a média dos valores de CPK dos pacientes com insuficiência
# cardíaca é superior a 450.5.4. Teste de Diferença de Médias
5.4.1. Amostras Independentes
O segundo teste paramétrico a ser estudo é o teste de hipóteses para diferença de médias para duas amostras independentes. Este teste, em geral, é utilizado para a comparação de grupos independentes, amostrados de populações diferentes ou da mesma população. Para a construção deste teste, há duas possibilidades: amostras grandes, e amostras pequenas. No primeiro caso, o teste será baseado na estatística Z, e no segundo caso, o nosso teste será baseado na estatística T. As Tabelas 5 e 6 trazem um sumário deste tipo de teste em ambos os casos.
| Amostras Grandes | Teste Unilateral (Superior) | Teste Unilateral (Inferior) | Teste Bilateral |
|---|---|---|---|
| Hipótese | \(H_0: \mu_1 - \mu_2 \geq
D_0\) \(H_a: \mu_1 - \mu_2 < D_0\) |
\(H_0: \mu_1 - \mu_2 \leq
D_0\) \(H_a: \mu_1 - \mu_2 > D_0\) |
\(H_0: \mu_1 - \mu_2 =
D_0\) \(H_a: \mu_1 -\mu_2 \neq D_0\) |
| Estatística do Teste | \(z_{calc} = \dfrac{(\bar{x}_1 - \bar{x}_2) - D_0}{\sigma_{\bar{x}_1 - \bar{x}_2}}\) | ||
| Região Crítica | Rejeita-se \(H_0\) se \(z_{calc} \leq -z_{1-\alpha}\) | Rejeita-se \(H_0\) se \(z_{calc} \geq z_{1-\alpha}\) | Rejeita-se \(H_0\) se
\(z_{calc} \leq -z_{1-\alpha/2}\)
ou se \(z_{calc} \geq z_{1-\alpha/2}\) |
| Amostras Pequenas | Teste Unilateral (Superior) | Teste Unilateral (Inferior) | Teste Bilateral |
|---|---|---|---|
| Hipótese | \(H_0: \mu_1 - \mu_2 \geq
D_0\) \(H_a: \mu_1 - \mu_2 < D_0\) |
\(H_0: \mu_1 - \mu_2 \leq
D_0\) \(H_a: \mu_1 - \mu_2 > D_0\) |
\(H_0: \mu_1 - \mu_2 =
D_0\) \(H_a: \mu_1 -\mu_2 \neq D_0\) |
| Estatística do Teste | \(t_{calc} = \dfrac{(\bar{x}_1 - \bar{x}_2) - D_0}{s_{\bar{x}_1 - \bar{x}_2}}\) | ||
| Região Crítica | Rejeita-se \(H_0\) se \(t_{calc} \leq -t_{\alpha}\) | Rejeita-se \(H_0\) se \(t_{calc} \geq t_{\alpha}\) | Rejeita-se \(H_0\) se
\(t_{calc} \leq -t_{\alpha/2}\) ou se \(t_{calc} \geq t_{\alpha/2}\) |
Observação: No caso de amostras pequenas (\(n_1 < 30\) e \(n_2 < 30\)), o erro-padrão é aproximado com base na combinação das amostras. Isto é, se \(s_1^2\) e \(s_2^2\) são as variâncias amostrais, tem-se que a variância combinada das amostras é descrito por:
\[s_p^2 = \dfrac{(n_1−1)s_1^2+(n_2−1)s_2^2}{n_1 + n_2 − 2}\]
Assim, tem-se que a aproximação do erro-padrão, \(\sigma_{\bar{X}_1 - \bar{X}_2}\), é dado por:
\[\sigma_{\bar{X}_1 - \bar{X}_2} = s_{\bar{X}_1 - \bar{X}_2} = \sqrt{s_p^2\left(\dfrac{1}{n_1} + \dfrac{1}{n_2}\right)}\]
Uma vez que na prática é mais comum o uso do teste t para comparação
de médias do que o teste Z, nesta seção, exemplificaremos apenas o teste
t para a diferença de médias no R. Neste caso, também fazemos o uso da
função t.test disponível no R que é uma função
baseada na distribuição t de Student com objetivo de criar intervalos de
confiança e testes de hipóteses para problemas com uma ou duas amostras
quando o desvio-padrão/variância populacional é desconhecido. Considere,
como exemplo, a seguinte situação:
Hoekema et. al (2003) estudaram a morfologia craniofacial de 20 pacientes do sexo masculino com síndrome da apneia obstrutiva do sono (SAOS). Uma das variáveis de interesse foi o comprimento do ponto mais súpero-anterior do corpo do osso hióide até a horizontal de Frankfurt.
| Comprimento do Osso (em mm) (Saudáveis) | Comprimento do Osso (em mm) (SAOS) | Comprimento do Osso (em mm) (Saudáveis) | Comprimento do Osso (em mm) (SAOS) |
|---|---|---|---|
| 96.80 | 105.95 | 97.00 | 114.90 |
| 100.70 | 114.90 | 97.70 | 114.35 |
| 94.55 | 110.35 | 97.00 | 112.25 |
| 99.65 | 123.10 | 94.55 | 106.15 |
| 109.15 | 119.30 | 106.45 | 102.60 |
| 102.75 | 110.00 | 94.55 | 102.40 |
| 97.70 | 98.95 | 94.05 | 105.05 |
| 92.10 | 114.20 | 89.45 | 112.65 |
| 89.50 | 105.05 | 98.20 | 117.70 |
Perguntas de Interesse:
Com base na amostra na tabela abaixo, existe evidência suficiente para concluirmos que, ao nível de significância de 5%, as duas populações amostradas diferem em relação ao comprimento do osso hióide até a horizontal de Frankfurt?
## Carregar os dados:
setwd('datasets')
db.osso <- read.csv('dataset-16-osso.csv', header = TRUE, sep = ',')
names(db.osso) <- c('Comprimento do Osso (em mm) (Saudáveis)', 'Comprimento do Osso (em mm) (SAOS)', 'Comprimento do Osso (em mm) (Saudáveis)', 'Comprimento do Osso (em mm) (SAOS)')
db.osso## Comprimento do Osso (em mm) (Saudáveis) Comprimento do Osso (em mm) (SAOS)
## 1 96.80 105.95
## 2 100.70 114.90
## 3 94.55 110.35
## 4 99.65 123.10
## 5 109.15 119.30
## 6 102.75 110.00
## 7 97.70 98.95
## 8 92.10 114.20
## 9 89.50 105.05
## Comprimento do Osso (em mm) (Saudáveis) Comprimento do Osso (em mm) (SAOS)
## 1 97.00 114.90
## 2 97.70 114.35
## 3 97.00 112.25
## 4 94.55 106.15
## 5 106.45 102.60
## 6 94.55 102.40
## 7 94.05 105.05
## 8 89.45 112.65
## 9 98.20 117.70
## Teste de Hipóteses
# Uso da função t.test():
# t.test(x, y = NULL, alternative = c("two.sided", "less", "greater"), mu = 0,
# paired = FALSE, var.equal = FALSE, conf.level = 0.95, ...)
saud <- c(db.osso[,1], db.osso[,3])
SAOS <- c(db.osso[,2], db.osso[,4])
t.test(x = saud, y = SAOS, alternative = 'two.sided', mu = 0, conf.level = 0.95, var.equal = TRUE)##
## Two Sample t-test
##
## data: saud and SAOS
## t = -6.7365, df = 34, p-value = 9.68e-08
## alternative hypothesis: true difference in means is not equal to 0
## 95 percent confidence interval:
## -17.211083 -9.233362
## sample estimates:
## mean of x mean of y
## 97.3250 110.5472
## Valor de T tabelado para hipótese:
GL <- 18 + 18 - 2 # Graus de Liberdade
qt(0.025, GL)## [1] -2.032245
## Conclusão:
# Como o valor calculado para T, -6.7365, é menor do que o valor tabelado de T, -2.032, rejeita-se H0.
# Isto é, ao nível de significância de 5%, evidência suficiente para concluirmos que, ao nível de
# significância de 5%, as duas populações amostradas diferem em relação ao comprimento do osso hióide
# até a horizontal de Frankfurt.5.4.2. Amostras Pareadas
O nosso último teste paramétrico a ser estudo é o teste de hipóteses para diferença de médias para duas amostras pareadas. Este teste, em geral, é utilizado para comprovar se determinado paciente teve melhora após a administração do medicamento, ou tratamento. Para a construção deste teste, há duas possibilidades: amostras grandes, e amostras pequenas. No primeiro caso, o teste será baseado na estatística Z, e no segundo caso, o nosso teste será baseado na estatística T. As Tabelas 8 e 10 trazem um sumário deste tipo de teste em ambos os casos.
| Amostras Grandes | Teste Unilateral (Superior) | Teste Unilateral (Inferior) | Teste Bilateral |
|---|---|---|---|
| Hipótese | \(H_0: \mu_{antes} -
\mu_{depois} \geq D_0\) \(H_a: \mu_{antes} - \mu_{depois} < D_0\) |
\(H_0: \mu_{antes} -
\mu_{depois} \leq D_0\) \(H_a: \mu_{antes} - \mu_{depois} > D_0\) |
\(H_0: \mu_{antes} -
\mu_{depois} = D_0\) \(H_a: \mu_{antes} - \mu_{depois} \neq D_0\) |
| Estatística do Teste | \(z_{calc} = \dfrac{\bar{d} - D_0}{s_d/\sqrt{n_d}}\) | ||
| Região Crítica | Rejeita-se \(H_0\) se \(z_{calc} \leq -z_{1-\alpha}\) | Rejeita-se \(H_0\) se \(z_{calc} \geq z_{1-\alpha}\) | Rejeita-se \(H_0\) se
\(z_{calc} \leq -z_{1-\alpha/2}\)
ou se \(z_{calc} \geq z_{1-\alpha/2}\) |
| Amostras Pequenas | Teste Unilateral (Superior) | Teste Unilateral (Inferior) | Teste Bilateral |
|---|---|---|---|
| Hipótese | \(H_0: \mu_{antes} -
\mu_{depois} \geq D_0\) \(H_a: \mu_{antes} - \mu_{depois} < D_0\) |
\(H_0: \mu_{antes} -
\mu_{depois} \leq D_0\) \(H_a: \mu_{antes} - \mu_{depois} > D_0\) |
\(H_0: \mu_{antes} -
\mu_{depois} = D_0\) \(H_a: \mu_{antes} - \mu_{depois} \neq D_0\) |
| Estatística do Teste | \(t_{calc} = \dfrac{\bar{d} - D_0}{s_d/\sqrt{n_d}}\) | ||
| Região Crítica | Rejeita-se \(H_0\) se \(t_{calc} \leq -t_{\alpha}\) | Rejeita-se \(H_0\) se \(t_{calc} \geq t_{\alpha}\) | Rejeita-se \(H_0\) se
\(t_{calc} \leq -t_{\alpha/2}\) ou se \(t_{calc} \geq t_{\alpha/2}\) |
Uma vez que na prática é mais comum o uso do teste t para comparação
de médias do que o teste Z, nesta seção, exemplificaremos apenas o teste
t para a diferença de médias de amostras pareadas no R. Neste caso,
também fazemos o uso da função t.test disponível no
R que é uma função baseada na distribuição t de Student com
objetivo de criar intervalos de confiança e testes de hipóteses para
problemas com uma ou duas amostras quando o desvio-padrão/variância
populacional é desconhecido. No entanto, há uma sutil diferença do uso
em relação ao caso das amostras independentes. Agora é necessário o uso
do argumento paired = TRUE para indicar que é uma amostra
pareada. Como exemplo, a seguinte situação:
Procellini et. al (2003) investigaram o efeito na contagem de células T-CD4 após administração de interleucina intermitente (IL-2) em adição à terapia antirretroviral altamente ativa (HAART). Na tabela abaixo são exibidas as contagens de células T-CD4 antes e depois da terapia.
| ID do Paciente | Células T-CD4 (Início) | Células T-CD4 (HAART) |
|---|---|---|
| 1 | 173 | 257 |
| 2 | 58 | 108 |
| 3 | 103 | 315 |
| 4 | 181 | 362 |
| 5 | 105 | 141 |
| 6 | 301 | 549 |
| 7 | 169 | 369 |
Perguntas de Interesse:
Com base nessa amostra, existe evidência suficiente para concluirmos que, ao nível de significância de 5%, há uma alteração significativa na contagem de células T-CD4 após o uso da terapia HAART com IL-2?
## Carregar os dados:
setwd('datasets')
db.haart <- read.csv('dataset-15-haart.csv', header = TRUE, sep = ',')
names(db.haart) <- c('ID do Paciente', 'Células T-CD4 (Início)', 'Células T-CD4 (HAART)')
db.haart## ID do Paciente Células T-CD4 (Início) Células T-CD4 (HAART)
## 1 1 173 257
## 2 2 58 108
## 3 3 103 315
## 4 4 181 362
## 5 5 105 141
## 6 6 301 549
## 7 7 169 369
## Teste de Hipóteses
# Uso da função t.test():
# t.test(x, y = NULL, alternative = c("two.sided", "less", "greater"), mu = 0,
# paired = FALSE, var.equal = FALSE, conf.level = 0.95, ...)
t.test(x = db.haart[,2], y = db.haart[,3], alternative = 'two.sided', mu = 0, conf.level = 0.95, paired = TRUE)##
## Paired t-test
##
## data: db.haart[, 2] and db.haart[, 3]
## t = -4.46, df = 6, p-value = 0.004283
## alternative hypothesis: true mean difference is not equal to 0
## 95 percent confidence interval:
## -223.66696 -65.19018
## sample estimates:
## mean difference
## -144.4286
## Valor de T tabelado para hipótese:
GL <- 7 - 1 # Graus de Liberdade
qt(0.025, GL)## [1] -2.446912
## Conclusão:
# Como o valor calculado para T, -4.46, é menor do que o valor tabelado de T, -2.447, rejeita-se H0.
# Isto é, existe evidência suficiente para concluirmos que, ao nível de significância de 5%, há uma
# alteração significativa na contagem de células T-CD4 após o uso da terapia HAART com IL-2.5.5. Testes Qui-Quadrado
5.5.1. Teste \(\chi^2\) de Aderência
Considere a seguinte tabela de frequências, com \(k\geq 2\) categorias:
| Categorias | Frequência Observada | Frequência Esperada |
|---|---|---|
| 1 | \(O_1\) | \(E_1\) |
| 2 | \(O_2\) | \(E_2\) |
| \(\ldots\) | \(\ldots\) | \(\ldots\) |
| k | \(O_k\) | \(E_k\) |
| Total | \(n\) | \(n\) |
Em que \(O_i\) é o total de indivíduos observado, e \(E_i\) é o total de indivíduos esperados obtido por \(E_i=n\cdot p_{0i}\), em que \(p_{0i}\) é a probabilidade especificada pelo modelo probabilístico de interesse. Assim, com base nessa tabela, o teste \(\chi^2\) de aderência tem por objetivo testar a adequabilidade de um modelo probabilístico a um conjunto de dados observados, isto é, testar se a frequência observada é igual ou não a frequência esperada pelo modelo em cada uma das categorias. Então, da comparação de frequências, pode-se concluir que: se elas forem grandes, a \(H_0\) deverá ser rejeitada em favor da \(H_a\), mas se elas forem pequenas, a \(H_0\) não será rejeitada e as diferenças serão atribuíveis de acordo com o modelo utilizado.
| Teste Qui-Quadrado | Condições |
|---|---|
| Hipótese | Seja \(p_i\) a probabilidade associada à categoria \(i\), então \(H_0: p_i = p_{0i}\) vs. \(H_a:\) Ao menos uma probabilidade é diferente. |
| Estatística do Teste | \(\chi_{calc}^2 = \sum_{i=1}^{n}\dfrac{(O_i - E_i)^2}{E_i}\) |
| Região Crítica | Com base no modelo qui-quadrado com \(k-1\) graus de liberdade, rejeita-se \(H_0\) se \(\chi_{calc}^2 \geq \chi_{tab}^2\) |
No R, este teste pode ser realizado por meio da função
chisq.test em que podemos declarar as probabilidades do
modelo desejado pelo argumento p para testar a aderência de
um determinado modelo de probabilidade. Para exemplificar o teste,
considere a seguinte situação-problema:
O ácido úrico está entre as substâncias naturalmente produzidas pelo organismo. Ele surge como resultado da quebra das moléculas de purina – proteína contida em muitos alimentos – por ação de uma enzima chamada xantina oxidase. Os níveis de ácido úrico no sangue sobem, em geral, porque o paciente está eliminando pouco pela urina ou por interferência do uso de certos medicamentos. Neste contexto, na tabela abaixo, é apresentado a distribuição das concentrações de ácido úrico tomadas em 250 pacientes.
| Ácido Úrico | Frequência Observada |
|---|---|
| < 1 | 1 |
| 1 até 1,99 | 5 |
| 2 até 2,99 | 15 |
| 3 até 3,99 | 24 |
| 4 até 4,99 | 43 |
| 5 até 5,99 | 50 |
| 6 até 6,99 | 45 |
| 7 até 7,99 | 30 |
| 8 até 8,99 | 22 |
| 9 ou mais | 15 |
| Total | 250 |
Pergunta de Interesse:
O modelo normal com média \((\mu)\) igual à 5,74 e desvio-padrão \((\sigma)\) igual à 2,01 é adequado para explicar as concentrações de ácido úrico dos pacientes, ao nível de significância de 1%?
## Definir o vetor com as frequências observadas:
f_obs <- c(1,5,15,24,43,50,45,30,22,15)
## Definir o vetor com as probabilidades para cada categoria:
p1 <- pnorm(1, 5.74, 2.01)
p2 <- pnorm(1.99, 5.74, 2.01) - pnorm(1, 5.74, 2.01)
p3 <- pnorm(2.99, 5.74, 2.01) - pnorm(2, 5.74, 2.01)
p4 <- pnorm(3.99, 5.74, 2.01) - pnorm(3, 5.74, 2.01)
p5 <- pnorm(4.99, 5.74, 2.01) - pnorm(4, 5.74, 2.01)
p6 <- pnorm(5.99, 5.74, 2.01) - pnorm(5, 5.74, 2.01)
p7 <- pnorm(6.99, 5.74, 2.01) - pnorm(6, 5.74, 2.01)
p8 <- pnorm(7.99, 5.74, 2.01) - pnorm(7, 5.74, 2.01)
p9 <- pnorm(8.99, 5.74, 2.01) - pnorm(8, 5.74, 2.01)
p10 <- 1 - pnorm(9, 5.74, 2.01)
probs <- round(c(p1,p2,p3,p4,p5,p6,p7,p8,p9,p10),3) + 0.001
## Teste de Hipóteses
# Uso da função chisq.test():
# chisq.test(x, y = NULL, correct = TRUE, p = rep(1/length(x), length(x)),
# rescale.p = FALSE, simulate.p.value = FALSE, B = 2000)
chisq.test(x = f_obs, p = probs)## Warning in chisq.test(x = f_obs, p = probs): Chi-squared approximation may be
## incorrect
##
## Chi-squared test for given probabilities
##
## data: f_obs
## X-squared = 2.5755, df = 9, p-value = 0.9788
## Valor de X^2 tabelado para hipótese:
qchisq(0.99, 9)## [1] 21.66599
## Conclusão:
# Como o valor calculado para X^2, 2.5755, é menor do que o valor tabelado de X^2, 21.66599, não rejeita-se H0.
# Isto é, o modelo normal com média igual à 5,74 e desvio-padrão igual à 2,01 é adequado para explicar as
# concentrações de ácido úrico dos pacientes, ao nível de significância de 1%.5.5.2. Teste \(\chi^2\) de Independência
Seguimos agora para a segunda categoria de testes não-paramétricos, os testes para duas amostras independentes. Nessa categoria, o primeiro teste que temos é o teste \(\chi^2\) de independência. Este teste é utilizado para avaliar a dependência ou independência entre as variáveis sendo consideradas.
Para realizar este teste, as variáveis devem estar tabuladas em forma de tabelas de contingência de tamanho \(“k \times n”\), em que “k” representa o nº de linhas e “n” o de colunas. Um exemplo de tabela de contingência para o teste 𝜒^2 de independência é ilustrada na tabela abaixo.
| Categoria Y | Total | |||
|---|---|---|---|---|
| Categoria X | \(Y_1\) | \(\ldots\) | \(Y_s\) | |
| \(X_1\) | \(O_{11}\) | \(\ldots\) | \(O_{1s}\) | \(O_{1j}\) |
| \(X_1\) | \(O_{21}\) | \(\ldots\) | \(O_{2s}\) | \(O_{2j}\) |
| \(X_1\) | \(O_{31}\) | \(\ldots\) | \(O_{3s}\) | \(O_{3j}\) |
| \(\ldots\) | \(\ldots\) | \(\ldots\) | \(\ldots\) | \(\ldots\) |
| \(X_1\) | \(O_{r1}\) | \(\ldots\) | \(O_{rs}\) | \(O_{rj}\) |
| Total | \(O_{j1}\) | \(\ldots\) | \(O_{js}\) | \(n\) |
Neste caso, as frequências observadas são determinadas pela seguinte expressão:
\[E_{ij} = \dfrac{O_{ij} \cdot O_{ji}}{n}\]
Assim, com base na tabela anterior, o teste \(\chi^2\) de independência tem por objetivo testar se variáveis são independentes ou não com base em um conjunto de dados observados, isto é, testar se a frequência observada é igual ou não a frequência esperada entre as observações das variáveis em cada categoria. Então, da comparação de frequências, pode-se concluir que: se elas forem grandes, a \(H_0\) deverá ser rejeitada em favor da \(H_a\), mas se elas forem pequenas, a \(H_0\) não será rejeitada e as diferenças serão atribuíveis de acordo com o modelo utilizado.
| Teste Qui-Quadrado | Condições |
|---|---|
| Hipótese | \(H_0\): As variáveis X e Y são independentes vs. \(H_a\): As variáveis X e Y não são independentes |
| Estatística do Teste | \(\chi_{calc}^2 = \sum_{i=1}^{n}\dfrac{(O_{ij} - E_{ij})^2}{E_{ij}}\) |
| Região Crítica | Com base no modelo qui-quadrado com \((r-1)(s-1)\) graus de liberdade, rejeita-se \(H_0\) se \(\chi_{calc}^2 \geq \chi_{tab}^2\) |
No R, este teste pode ser realizado também por meio da função
chisq.test, só que agora iremos criar uma matriz referente
a tabela de contingência dos dados, e usando o argumento
correct = FALSE (este argumento deve ser utilizado como
TRUE apenas se a correção de Yates for necessária). Para
exemplificar o teste, considere a seguinte situação-problema:
A infecção do sítio cirúrgico esternal (ISC) após cirurgia de revascularização miocárdica é uma complicação que aumenta a morbidade do paciente. Neste aspecto, Segal e Anderson (2002) realizaram um estudo que examinou dois tipos de preparação pré-operatória da pele antes da realização de cirurgia cardíaca.
|
Tipo de Preparação
|
|||
|---|---|---|---|
| Grupo de Preparação | Iodo Aquoso | Iodo Insolúvel | Total |
| Infectado | 14 | 4 | 18 |
| Não-Infectado | 94 | 97 | 191 |
| Total | 108 | 101 | 209 |
Pergunta de Interesse:
Esses dados fornecem evidência suficiente, ao nível de significância de 5%, para justificar a conclusão de que o tipo de preparação da pele e a infecção estão relacionados?
## Definir o vetor com as frequências observadas:
f_obs <- matrix(c(14,94,4,97), ncol = 2)
## Teste de Hipóteses
# Uso da função chisq.test():
# chisq.test(x, y = NULL, correct = TRUE, p = rep(1/length(x), length(x)),
# rescale.p = FALSE, simulate.p.value = FALSE, B = 2000)
chisq.test(x = f_obs, p = probs)##
## Pearson's Chi-squared test with Yates' continuity correction
##
## data: f_obs
## X-squared = 4.2913, df = 1, p-value = 0.03831
## Valor de X^2 tabelado para hipótese:
qchisq(0.95, 1)## [1] 3.841459
## Conclusão:
# Como o valor calculado para X^2, 5.3743, é maior do que o valor tabelado de X^2, 3.8414, rejeita-se H0.
# Isto é, ao nível de significância de 5%, pode-se concluir que não há evidências suficientes para afirmar
# que há uma associação entre o tipo de preparação da pele e a infecção.5.6. Teste de Mantel-Haenzel
O segundo teste, o teste de Mantel-Haenszel, foi desenvolvido por N. Mantel e W. Haenszel no ano de 1959. Tal teste é uma técnica que gera uma estimativa de uma associação entre uma doença e um fator de risco após o ajuste ou tendo em conta a confusão. Neste teste, os dados a serem analisados consistem de várias tabelas de contingência (Tabela 2), em vez de apenas uma. Se for apropriado, o teste fornecerá um meio de calcularmos uma estimativa por ponto ou um intervalo de confiança para o odds ratio da população global. E, além disso, nos permitirá testar a hipótese nula de não-associação entre a exposição e a doença.
|
Ocorrência da Doença
|
|||
|---|---|---|---|
| Fator de Risco | Presente | Ausente | Total |
| Presente | Ai | Bi | Ai + Bi |
| Ausente | Ci | Di | Ci + Di |
| Total | Ai + Ci | Bi + Di | Ni |
O teste de Mantel-Haenzel, dois pressupostos devem ser satisfeitos: (1) as observações são independentes; (2) todas as observações devem ser identicamente distribuídas. Cumpridos esses pressupostos, podemos então prosseguir com o teste de Mantel-Haenzel. Para facilitar nosso entendimento deste teste, ele será dividido em etapas. A primeira etapa do teste de Mantel-Haenszel, então, é fornecer uma estimativa pontual para o estimador da odds ratio do mesmo modo que foi feito para as distribuições amostrais. Neste caso, com base na tabela de contingência defina na Tabela 2, tem-se que o estimador da odds ratio é dado por:
\[\widehat{OM}_{MH} = \dfrac{\sum_{i=1}^k\frac{A_i D_i}{N_i}}{\sum_{i=1}^k\frac{B_i C_i}{N_i}}\]
A partir desse estimador, podemos definir um intervalo de \(100\cdot (1 - \alpha)\%\) de confiança, em torno de \(\widehat{OM}_{MH}\), para o método de Mantel-Haenszel. Porém para estimar o intervalo de confiança, precisamos de um estimador da variância em torno de \(\widehat{OM}_{MH}\). Então, a partir da Tabela 2, tem-se:
\[Var(\widehat{OM}_{MH}) = \dfrac{\sum_{i=1}^k A_i D_i\left(\frac{A_i + D_i}{N_i}\right)}{2\left(\sum_{i=1}^k\frac{A_i D_i}{N_i}\right)^2} + \dfrac{\sum_{i=1}^k A_i D_i(B_i + C_i) + B_i C_i(A_i + D_i)}{2N_{i}^2\sum_{i=1}^k\frac{A_i D_i}{N_i} \sum_{i=1}^k\frac{B_i C_i}{N_i}} + \dfrac{\sum_{i=1}^k B_i C_i\left(\frac{B_i + C_i}{N_i}\right)}{2\left(\sum_{i=1}^k\frac{B_i C_i}{N_i}\right)^2}\]
Assim como feito na distribuição amostral, o nosso estimador precisa ser corrigido de acordo com o tamanho amostral. Neste caso, então, a correção do estimador é descrita por:
\[Var(\ln(\widehat{OM}_{MH})) = \dfrac{Var(\widehat{OM}_{MH})}{(\widehat{OM}_{MH})^2}\]
E, assim, o intervalo de 95% confiança para a odds ratio, em torno de \(\widehat{OM}_{MH}\), é descrito por:
\[IC_{\widehat{OM}_{MH}} = e^{\ln(\widehat{OM}_{MH})} \pm 1,96 \cdot \sqrt{Var(\ln(\widehat{OM}_{MH}))}\]
A a segunda etapa do teste de Mantel-Haenszel consiste em combinar a informação de duas ou mais tabelas de contingências afim de testar se o odds ratio é igual a 1. Assim, se o odds ratio for igual a 1 significa que não há associação entre a doença e o fator de risco. Neste caso, então, definimos as hipóteses:
\[ H_0: \widehat{OM}_{MH} = 1\] \[vs.\] \[H_a: \widehat{OM}_{MH} \neq 1\]
Agora, para cada combinação das tabelas de contingências, pode-se calcular o estimador do valor esperado, da célula superior esquerda da seguinte forma:
\[\hat{E}_i = \dfrac{(A_i+B_i)(A_i+C_i)}{N_i}\]
E o estimador da variância, neste caso, pode ser calculado como:
\[\widehat{Var}_i \dfrac{(A_i+B_i)(A_i+C_i)(B_i + D_i)(C_i + D_i)}{N_{i}^2(N_i - 1)}\]
Com base nesses resultados, podemos então prosseguir com a estatística do teste de Mantel-Haenzel. Tal estatística será baseada na estatística \(\chi^2\) e, neste caso, será definida como:
\[\chi^2_{MH} = \dfrac{\left(\sum_{i=1}^{k} A_i - \sum_{i=1}^{k} \hat{E}_i\right)^2}{\sum_{i=1}^{k}\widehat{Var}_i}\]
Assim, rejeitamos a hipótese nula se o valor de \(\chi^2_{MH}\) for igual ou maior do que o valor crítico da estatística do teste, que é o valor do qui-quadrado tabelado para 1 grau de liberdade e o nível de significância escolhido.
| Teste Mantel-Haenszel | Condições |
|---|---|
| Hipótese | \(H_0\): Não há associação entre o fator de risco e a doença vs. \(H_a\): Há associação entre o fator de risco e a doença |
| Estatística do Teste | \(\chi^2_{MH} = \dfrac{\left(\sum_{i=1}^{k} A_i - \sum_{i=1}^{k} \hat{E}_i\right)^2}{\sum_{i=1}^{k}\widehat{Var}_i}\) |
| Região Crítica | Com base no modelo qui-quadrado com 1 grau de liberdade, rejeita-se \(H_0\) se \(\chi_{MH}^2 \geq \chi_{tab}^2\) |
No R, este teste pode ser realizado por meio da função
mantelhaen.test, sendo necessário criar um
array referente a tabela de contingência dos dados, e
usando o argumento correct = FALSE (este argumento deve ser
utilizado como TRUE apenas se a correção de Yates for
necessária). Para exemplificar o teste, considere a seguinte
situação-problema:
Em seu estudo de LaMont et. al (2002), coletaram dados sobre doença arterial coronariana obstrutiva (OCAD), hipertensão e idade entre os pacientes identificados por um teste de estresse em esteira como estando em risco. Os dados do estudo estão dispostos na tabela abaixo.
|
Estrato 1: Abaixo de 55 anos
|
|||
|---|---|---|---|
|
OCAD
|
|||
| Hipertensão | Casos | Não-Casos | Total |
| Presente | 21 | 11 | 32 |
| Ausente | 16 | 6 | 22 |
| Total | 37 | 17 | 54 |
|
Estrato 2: Acima de 55 anos
|
|||
|---|---|---|---|
|
OCAD
|
|||
| Hipertensão | Casos | Não-Casos | Total |
| Presente | 50 | 14 | 64 |
| Ausente | 18 | 6 | 24 |
| Total | 68 | 20 | 88 |
Pergunta de Interesse:
Esses dados fornecem evidência suficiente, ao nível de significância de 5%, para justificar a conclusão de que não existe uma associação entre a presença de hipertensão e a ocorrência de OCAD nos pacientes com idade abaixo ou acima de 55 anos?
## Definir o vetor com as frequências observadas:
data_MH <- array(c(21,16,11,6,50,18,14,6),
dim = c(2,2,2),
dimnames = list(Hipertensao = c("Presente","Ausente"),
OCAD = c("Casos","Nao_Casos"),
Idade = c("< 55_Anos", "> 55_Anos")))
## Teste de Hipóteses
# Uso da função chisq.test():
# mantelhaen.test(x, y = NULL, z = NULL, alternative = c("two.sided", "less", "greater"),
# correct = TRUE, exact = FALSE, conf.level = 0.95)
mantelhaen.test(data_MH, correct = FALSE)##
## Mantel-Haenszel chi-squared test without continuity correction
##
## data: data_MH
## Mantel-Haenszel X-squared = 0.024265, df = 1, p-value = 0.8762
## alternative hypothesis: true common odds ratio is not equal to 1
## 95 percent confidence interval:
## 0.4184851 2.1018264
## sample estimates:
## common odds ratio
## 0.9378609
## Valor de X^2 tabelado para hipótese:
qchisq(0.95, 1)## [1] 3.841459
## Conclusão:
# Como o valor calculado para X^2, 0.024265, é menor do que o valor tabelado de X^2, 3.8414, não rejeita-se H0.
# Isto é, ao nível de significância de 5%, pode-se concluir que não há evidências suficientes para afirmar
# que uma associação entre a presença de hipertensão e a ocorrência de OCAD nos pacientes, de acordo com a idade.5.7. Teste de Mann-Whitney
Este teste, em geral, é utilizado para comprovar se dois grupos independentes foram ou não extraídos da mesma população, sendo o análogo não-paramétrico do teste paramétrico t de Student. Para a construção deste teste, considere \(n_1\) = número de casos no menor dos dois grupos independentes e \(n_2\) = número de casos no maior grupo. Primeiramente, combinam-se as observações ou escores de ambos os grupos, relacionando-os por ordem ascendente.
Nessa ordenação ascendente, consideram-se os valores algébricos do grupo \(n = n_1 + n_2\), isto é, os postos mais baixos são atribuídos aos maiores valores (negativos se houver). Agora, focaliza-se agora um dos grupos, por exemplo, o grupo que apresenta \(n_1\) casos. Logo, a estatística teste, U, será dada pelo número de vezes que um escore no grupo com \(n_2\) casos precede um escore no grupo com \(n_1\) casos no grupo ordenado formado por \(n = n_1 + n_2\) casos. Aqui, consideraremos o cálculo da estatística apenas para o caso em que \(n \leq 20\) (amostras pequenas) e com hipóteses do tipo bilateral.
Neste caso, a estatística do teste de Mann-Whitney pode escrita, de modo geral, pelas seguintes expressões:
\[U_1 = n_1 n_2 + \dfrac{n_1(n_1 + 1)}{2} − R_1\]
e,
\[U_2 = n_1 n_2 + \dfrac{n_2(n_2 + 1)}{2} − R_2\]
em que \(R_i\) é a soma dos postos grupo \(i = 1, 2\). A regra de decisão, assim como nos testes paramétricos e qui-quadrado, também será baseada em uma tabela de valores críticos, chamada de tabela U.
| 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 2 | . | . | . | 0 | 0 | 0 | 0 | 1 | 1 | 1 | 1 | 1 | 2 | 2 | 2 | 2 |
| 3 | 0 | 1 | 1 | 2 | 2 | 3 | 3 | 4 | 4 | 5 | 5 | 6 | 6 | 7 | 7 | 8 |
| 4 | 1 | 2 | 3 | 4 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 11 | 12 | 13 | 14 |
| 5 | 2 | 3 | 5 | 6 | 7 | 8 | 9 | 11 | 12 | 13 | 14 | 15 | 17 | 18 | 19 | 20 |
| 6 | . | 5 | 6 | 8 | 10 | 11 | 13 | 14 | 16 | 17 | 19 | 21 | 22 | 24 | 25 | 27 |
| 7 | . | . | 8 | 10 | 12 | 14 | 16 | 18 | 20 | 22 | 24 | 26 | 28 | 30 | 32 | 34 |
| 8 | . | . | . | 13 | 15 | 17 | 19 | 22 | 24 | 26 | 29 | 31 | 34 | 36 | 38 | 41 |
| 9 | . | . | . | . | 17 | 20 | 23 | 26 | 28 | 31 | 34 | 37 | 39 | 42 | 45 | 48 |
| 10 | . | . | . | . | . | 23 | 26 | 29 | 33 | 36 | 39 | 42 | 45 | 48 | 52 | 55 |
| 11 | . | . | . | . | . | . | 30 | 33 | 37 | 40 | 44 | 47 | 51 | 55 | 58 | 62 |
| 12 | . | . | . | . | . | . | . | 37 | 41 | 45 | 49 | 53 | 57 | 61 | 65 | 69 |
| 13 | . | . | . | . | . | . | . | . | 45 | 50 | 54 | 59 | 63 | 67 | 72 | 76 |
| 14 | . | . | . | . | . | . | . | . | . | 55 | 59 | 64 | 69 | 74 | 78 | 83 |
| 15 | . | . | . | . | . | . | . | . | . | . | 64 | 70 | 75 | 80 | 85 | 90 |
| 16 | . | . | . | . | . | . | . | . | . | . | . | 75 | 81 | 86 | 92 | 98 |
| 17 | . | . | . | . | . | . | . | . | . | . | . | . | 87 | 93 | 99 | 105 |
| 18 | . | . | . | . | . | . | . | . | . | . | . | . | . | 99 | 106 | 112 |
| 19 | . | . | . | . | . | . | . | . | . | . | . | . | . | . | 113 | 119 |
| 20 | . | . | . | . | . | . | . | . | . | . | . | . | . | . | . | 127 |
| Teste de Mann-Whitney | Condições |
|---|---|
| Hipótese | \(H_0\): Os grupos apresentam a mesma distribuição vs. \(H_a\): Os grupos não apresentam a mesma distribuição |
| Estatística do Teste | \(U_{calc} = \min(U_1, U_2)\) |
| Região Crítica | Com base no nível de significância de 5%, rejeita-se \(H_0\) se \(U_{calc} \leq U_{tab}\) |
Para trabalhar com este teste no R, fazemos o uso da função
wilcox.test. Por ser um teste análogo ao teste paramétrico
t de Student para diferença de médias, essa função, no R, tem seu
funcionamento examente igual à função t.test para amostras
independentes. Assim, para exemplificar como ela funciona, vamos
considerar o seguinte exemplo:
A tianeptina é um fármaco antidepressivo do grupo dos tricíclicos. Sua ação antidepressiva potencial foi demonstrada em estudos pré-clínicos através de testes em animais. Rocha (1995) relata os resultados de um ensaio clínico aleatorizado, duplo-cego, realizado com o objetivo de comparar a tianeptina com o placebo. O ensaio constituiu em administrar a droga a dois grupos de pacientes, compostos de forma aleatória, e quantificar a depressão em que os valores maiores indicam maior gravidade da depressão. Os escores foram obtido para cada paciente 7, 14, 21, 28 e 42 dias após o início do estudo, e estão dispostos abaixo.
| Placebo | Tianeptina |
|---|---|
| 3 | 9 |
| 4 | 7 |
| 5 | 5 |
| 2 | 10 |
| 6 | 8 |
| 2 | 8 |
| 5 | 9 |
| 10 | 2 |
| 8 | 7 |
| 9 | 6 |
| 10 | 12 |
Pergunta de Interesse:
Ao nível de 5% de significância, pode-se concluir que há diferença significativa entre os escores dos medicamentos, indicando que um medicamento possui uma eficácia melhor que o outro no controle da depressão?
## Carregar os dados:
placebo <- c(3,4,5,2,6,2,5,10,8,9,10)
tianeptina <- c(9,7,5,10,8,8,9,2,7,6,12)
## Teste de Hipóteses
# Uso da função wilcox.test():
# wilcox.test(x, y = NULL, alternative = c("two.sided", "less", "greater"),
# mu = 0, paired = FALSE, exact = NULL, correct = TRUE,
# conf.int = FALSE, conf.level = 0.95,
# tol.root = 1e-4, digits.rank = Inf, ...)
wilcox.test(x = placebo, y = tianeptina, alternative = 'two.sided', mu = 0, conf.level = 0.95, paired = TRUE)## Warning in wilcox.test.default(x = placebo, y = tianeptina, alternative =
## "two.sided", : cannot compute exact p-value with ties
## Warning in wilcox.test.default(x = placebo, y = tianeptina, alternative =
## "two.sided", : cannot compute exact p-value with zeroes
##
## Wilcoxon signed rank test with continuity correction
##
## data: placebo and tianeptina
## V = 15, p-value = 0.2201
## alternative hypothesis: true location shift is not equal to 0
## Conclusão:
# Como o valor calculado para U, U_calc = 41.5, é maior do que o valor tabelado de U para amostras de
# tamanho 11, U_tab = 30, não rejeita-se H0. Isto é, ao nível de 5% de significância, pode-se concluir
# que não há diferença significativa entre os escores dos medicamentos, indicando que um medicamento
# não possui uma eficácia melhor que o outro no controle da depressão.5.8. Teste de Wilcoxon
O nosso último teste não-paramétrico para duas amostras, no caso pareadas, é o teste de Wilcoxon. Este teste baseia-se nos postos das diferenças entrapares, e é utilizado para comparar as medianas de amostras pareadas, sendo o análogo não-paramétrico do teste t pareado.
Para a construção deste teste, considere que \(X_i\) representa os escores da amostra 1 e \(y_i\) os escores da amostra 2. Primeiramente, são calculadas as diferenças de cada par de escores, \(d_i = X_i–Y_i\). Em seguida, atribuem-se postos às diferenças dos escores em valor absoluto, \(|d_i|\).
A menor diferença em valor absoluto receberá o posto 1, a segunda menor diferença em valor absoluto receberá o posto 2 e assim por diante. Depois, acrescenta-se o sinal das diferenças, \(d_i\), aos postos. Assim, sob a hipótese nula \((H_0)\), a soma dos postos positivos “+” e a soma dos postos negativos “-” devem ser aproximadamente iguais, indicando que não existe diferença entre as medianas dos grupos, se as somas não forem aproximadamente iguais pode-se dizer que as medianas dos grupos são diferentes, rejeitando-se \(H_0\).
Os empates podem ocorrer de duas maneiras: quando a diferença dos escores X e Y for zero \((|d_i| = 0)\), retira-se o par da análise. Quando houver diferenças em valor absoluto, \(|d_i|\), iguais, atribuem-se à essas diferenças a média dos postos que elas receberiam se não fossem empatadas e depois acrescentam-se aos postos os sinais das diferenças. Por fim, as hipóteses do teste são definidas como:
\[H_0: \sum p_i^{+}= \sum p_i^{−}\] \[vs.\] \[H_a: \sum p_i^{+} \neq (\text{ou } >, \text{ou } <) \sum p_i^{−}\]
Naturalmente, este teste pode ser aplicado tanto à pequenas amostras, quanto à grandes amostras. No entanto, nosso foco será apenas para o caso das pequenas amostras \((n\leq 20)\). Então, para \(n\leq 20\), tem-se que a estatística do teste é dada por:
\[𝑊 = \sum_{i=1}^{m} p_i^{+}\] em que \(m\) é o número de \(d_i^{+}\) e \(p_i^{+}\) é o posto (ordem) de \(|d_i|\) positivo. A regra de decisão, será baseada em uma tabela de valores críticos (Tabela W).
|
Bilateral
|
Unilateral
|
|||
|---|---|---|---|---|
| n | 0.05 | 0.01 | 0.05 | 0.01 |
| 5 | . | . | 0.00 | . |
| 6 | 0 | . | 2.00 | . |
| 7 | 2 | . | 3.00 | 0 |
| 8 | 3 | 0 | 5.00 | 1 |
| 9 | 5 | 1 | 8.00 | 3 |
| 10 | 8 | 3 | 10.00 | 5 |
| 11 | 10 | 5 | 13.00 | 7 |
| 12 | 13 | 7 | 17.00 | 9 |
| 13 | 17 | 9 | 21.00 | 12 |
| 14 | 21 | 12 | 25.00 | 15 |
| 15 | 25 | 15 | 30.00 | 19 |
| 16 | 29 | 19 | 35.00 | 23 |
| 17 | 34 | 23 | 41.00 | 27 |
| 18 | 40 | 27 | 47.00 | 32 |
| 19 | 46 | 32 | 53.00 | 37 |
| 20 | 52 | 37 | 60.00 | 43 |
| 21 | 58 | 42 | 67.00 | 49 |
| 22 | 65 | 48 | 75.00 | 55 |
| 23 | 73 | 54 | 83.00 | 62 |
| 24 | 81 | 61 | 91.00 | 69 |
| 25 | 89 | 68 | 100.00 | 76 |
| Teste de Wilcoxon | Teste Unilateral (Superior) | Teste Unilateral (Inferior) | Teste Bilateral |
|---|---|---|---|
| Hipótese | \(H_0: \sum p_i^{+}= \sum
p_i^{−}\) \(H_a: \sum p_i^{+}> \sum p_i^{−}\) |
\(H_0: \sum p_i^{+}= \sum
p_i^{−}\) \(H_a: \sum p_i^{+}< \sum p_i^{−}\) |
\(H_0: \sum p_i^{+}= \sum
p_i^{−}\) \(H_a: \sum p_i^{+}\neq \sum p_i^{−}\) |
| Estatística do Teste | \(W_{calc} = \sum_{i=1}^{m} p_i^{+}\) | ||
| Região Crítica | Rejeita-se \(H_0\) se \(W_{calc} \geq W_{\alpha}\) | Rejeita-se \(H_0\) se \(W_{calc} \geq W_{\alpha}\) | Rejeita-se \(H_0\) se \(W_{calc} \geq W_{\alpha/2}\) |
Para trabalhar com este teste no R, fazemos o uso da função
wilcox.test. Por ser um teste análogo ao teste paramétrico
t de Student para diferença de médias, essa função, no R, tem seu
funcionamento examente igual à função t.test para amostras
independentes. Assim, para exemplificar como ela funciona, vamos
considerar o seguinte exemplo:
Procellini et. al (2003) investigaram o efeito na contagem de células T-CD4 após administração de interleucina intermitente (IL-2) em adição à terapia antirretroviral altamente ativa (HAART). Na tabela abaixo são exibidas as contagens de células T-CD4 antes e depois da terapia.
| ID do Paciente | Células T-CD4 (Início) | Células T-CD4 (HAART) |
|---|---|---|
| 1 | 173 | 257 |
| 2 | 58 | 108 |
| 3 | 103 | 315 |
| 4 | 181 | 362 |
| 5 | 105 | 141 |
| 6 | 301 | 549 |
| 7 | 169 | 369 |
Perguntas de Interesse:
Com base nessa amostra, existe evidência suficiente para concluirmos que, ao nível de significância de 5%, há uma alteração significativa na contagem de células T-CD4 após o uso da terapia HAART com IL-2?
## Carregar os dados:
setwd('datasets')
db.haart <- read.csv('dataset-15-haart.csv', header = TRUE, sep = ',')
names(db.haart) <- c('ID do Paciente', 'Células T-CD4 (Início)', 'Células T-CD4 (HAART)')
db.haart## ID do Paciente Células T-CD4 (Início) Células T-CD4 (HAART)
## 1 1 173 257
## 2 2 58 108
## 3 3 103 315
## 4 4 181 362
## 5 5 105 141
## 6 6 301 549
## 7 7 169 369
## Teste de Hipóteses
# Uso da função t.test():
# wilcox.test(x, y = NULL, alternative = c("two.sided", "less", "greater"),
# mu = 0, paired = FALSE, exact = NULL, correct = TRUE,
# conf.int = FALSE, conf.level = 0.95,
# tol.root = 1e-4, digits.rank = Inf, ...)
wilcox.test(x = db.haart[,2], y = db.haart[,3], alternative = 'two.sided', mu = 0, conf.level = 0.95, paired = TRUE)##
## Wilcoxon signed rank exact test
##
## data: db.haart[, 2] and db.haart[, 3]
## V = 0, p-value = 0.01563
## alternative hypothesis: true location shift is not equal to 0
## Conclusão:
# Como o valor calculado para W, W_calc = 0, é menor do que o valor tabelado de W para amostras de
# tamanho 7, W_tab = 2, rejeita-se H0. Isto é, existe evidência suficiente para concluirmos que,
# ao nível de significância de 5%, há uma alteração significativa na contagem de células T-CD4
# após o uso da terapia HAART com IL-2.6. Análise de Regressão
6.1. Regressão Linear
A regressão linear é usada para prever o valor de uma variável de resultado Y com base em uma ou mais variáveis de previsão de entrada X. O objetivo é estabelecer uma relação linear (uma fórmula matemática) entre a(s) variável(is) de previsão e a variável de resposta, de modo que , podemos usar esta fórmula para estimar o valor da resposta Y, quando apenas os valores dos preditores (Xs) são conhecidos.
O objetivo da regressão linear é modelar uma variável contínua Y como uma função matemática de uma ou mais variáveis X, para que possamos usar esse modelo de regressão para prever o Y quando apenas o X é conhecido. Esta equação matemática pode ser generalizada da seguinte forma:
\[Y = \beta_0 + \beta_1 X + \epsilon\]
onde, \(\beta_0\) (intercepto) \(\beta_1\) (inclinação) são os coeficientes de regressão, e \(\epsilon\) é o termo de erro, a parte de Y que o modelo de regressão não consegue explicar.
No geral, quando trabalhamos com modelos de regressão linear, algumas suposições a respeito dos dados são necessárias, tais como:
- Linearidade dos dados: A relação entre o preditor (x) e o resultado (y) é assumida como linear.
- Normalidade dos resíduos: Os erros residuais são considerados normalmente distribuídos.
- Homogeneidade da variância dos resíduos: Supõe-se que os resíduos tenham uma variância constante (homocedasticidade)
- Independência dos termos de erro dos resíduos.
Então, para exemplificar como uma análise de regressão linear
funciona, em especial, no R, usaremos o conjunto de dados de
cars que está disponível no R. Esta base de dados consiste
em 50 observações, comtemplando as variáveis distância
percorrida (dist) e velocidade
(speed). Para visualiza-lá, basta usar o comando
head() do R:
head(cars)## speed dist
## 1 4 2
## 2 4 10
## 3 7 4
## 4 7 22
## 5 8 16
## 6 9 10
Antes de começarmos a construir o modelo de regressão, é uma boa prática analisar e entender as variáveis. Para esse fim, partiremos de duas análises importantes: a análise gráfica e o estudo da correlação entre as variáveis.
6.2. Análise Gráfica
O objetivo aqui é construir um modelo de regressão simples que
possamos usar para prever distância percorrida
(dist) estabelecendo uma relação linear estatisticamente
significativa com velocidade (speed). Mas antes de
pular para a sintaxe do modelo, vamos tentar entender essas variáveis
graficamente. Normalmente, para cada uma das variáveis independentes
(preditoras), os seguintes gráficos são desenhados para visualizar o
seguinte comportamento:
- Gráfico de dispersão: Visualiza a relação linear entre o preditor e a resposta.
- Box-plot: Para detectar quaisquer observações atípicas na variável. Ter outliers em seu preditor pode afetar drasticamente as previsões, pois podem afetar facilmente a direção/inclinação da linha de melhor ajuste.
- Gráfico de densidade: Para ver a distribuição da variável preditora. Idealmente, é preferível uma distribuição próxima à normal (uma curva em forma de sino), sem ser assimétrica para a esquerda ou para a direita. Vamos ver como fazer cada um deles.
6.2.1. Gráfico de Dispersão
Os gráficos de dispersão podem ajudar a visualizar quaisquer relações
lineares entre a variável dependente (resposta) e as variáveis
independentes (preditoras). Idealmente, se você tiver várias variáveis
preditoras, um gráfico de dispersão é desenhado para cada uma delas em
relação à resposta. Embora o gráfico de dispersão possa ser feito com a
função plot(), aqui utilizaremos a função
scatter.smooth() que traz a linha de suavização dos
dados.
## Uso da função scatter.smooth():
# scatter.smooth(x, y = NULL, span = 2/3, degree = 1,
# family = c("symmetric", "gaussian"),
# xlab = NULL, ylab = NULL,
# ylim = range(y, pred$y, na.rm = TRUE),
# evaluation = 50, ..., lpars = list())
scatter.smooth(x=cars$speed, y=cars$dist, main="Dist ~ Speed")O gráfico de dispersão junto com a linha de suavização acima sugere
uma relação linearmente crescente entre as variáveis dist e
speed. Isso é bom, porque uma das suposições subjacentes na
regressão linear é que a relação entre a resposta e as variáveis
preditoras é linear.
6.2.2. Box-Plot
Agora, iremos aos boxplots para cada uma das variáveis: resposta e
preditora. Por questões de visualizações, em nosso boxplot, uma função
adicional será considerada, a boxplot.stats. Essa função
descreve, como legenda em nosso gráfico, as linhas em que se encontram
possíveis outliers cujo seu conhecimento é fundamental quando
lidamos com modelos de regressão, especialmente os lineares.
par(mfrow=c(1, 2))
boxplot(cars$speed, main="Speed",
sub=paste("Outlier rows: ", boxplot.stats(cars$speed)$out)) # box plot para 'speed'
boxplot(cars$dist, main="Distance",
sub=paste("Outlier rows: ", boxplot.stats(cars$dist)$out)) # box plot para 'distance'6.3.3. Gráfico de Densidade
Anteriormente, vimos que o gráfico de densidade é uma
versão suavizada do histograma e é usado no mesmo conceito,
ou seja, para representar a distribuição de uma variável numérica. E
este gráfico é fundamental quando falamos de regressão para ver com mais
detalhes como se comportam as nossas variáveis, resposta e preditora, de
modo geral. Para nosso estudo aqui, iremos fazer o uso do pacote
e1071 para fazer o gráfico de densidade, pois ele traz o
cálculo da assimetria dos dados agregado ao gráfico, que torna a
descrição do comportamento mais interessante.
## Carregar o pacote
library(e1071)##
## Attaching package: 'e1071'
## The following object is masked from 'package:gtools':
##
## permutations
## The following objects are masked from 'package:agricolae':
##
## kurtosis, skewness
## Fazer o gráfico
par(mfrow=c(1, 2))
plot(density(cars$speed), main="Density Plot: Speed", ylab="Frequency",
sub=paste("Skewness:", round(e1071::skewness(cars$speed), 2))) # density plot para 'speed'
polygon(density(cars$speed), col="red")
plot(density(cars$dist), main="Density Plot: Distance", ylab="Frequency",
sub=paste("Skewness:", round(e1071::skewness(cars$dist), 2))) # density plot para 'dist'
polygon(density(cars$dist), col="red")6.3. Correlação
Correlação é uma medida estatística que sugere o grau de dependência linear entre duas variáveis, que ocorrem em par, podendo assumir valores entre -1 e 1. Ao calcular a correlação entre as nossas variáveis, pode-se notar que a mesma é positiva e mais próxima de 1 do que de 0, o que indica que há, neste caso, uma relação linear positiva entre as variáveis, conforme visualizamos no gráfico de dispersão anteriormente.
cor(cars$speed, cars$dist) ## [1] 0.8068949
6.4. Construindo o Modelo Linear
Agora que vimos o relacionamento linear no gráfico de dispersão e
calculando a correlação, vamos ver a sintaxe para construir o modelo
linear. No R, a função utilizada para a construção de modelos lineares é
lm(). A função lm() recebe dois argumentos
principais: formula, e data. Em geral, o
argumento data recebe como objeto um
data.frame e o argumento formula recebe um
objeto de classe fórmula. No entanto, a convenção mais comum é
simplesmente escrever a fórmula diretamente no lugar do argumento
formula, conforme a rotina abaixo.
linearMod <- lm(dist ~ speed, data = cars)
print(linearMod)##
## Call:
## lm(formula = dist ~ speed, data = cars)
##
## Coefficients:
## (Intercept) speed
## -17.579 3.932
Agora que construímos o modelo linear, também estabelecemos a relação
entre o preditor e a resposta na forma de uma fórmula matemática para
distância (dist) como uma função para velocidade
(speed). Assim, para a saída acima, você pode notar que a
parte ‘Coeficientes’ tem dois componentes: Intercept: -17,579, speed:
3,932. Estes coeficientes também são chamados de coeficientes beta do
nosso modelo. Em outras palavras,
\[dist = Intercept + (\beta_1 \cdot speed) = \beta_0 + \beta_1 speed \] Com os valores betas obtidos, tem-se que
\[dist = −17.579 + 3.932 \cdot speed\]
6.5. Diagnóstico do Modelo Linear
Agora o modelo linear está construído e temos uma fórmula que podemos
usar para prever o valor da distância se uma velocidade correspondente
for conhecida. A nossa primeira pergunta é: Isso é o suficiente para
realmente usar este modelo? A resposta imediata para essa pergunta
é NÃO! Antes de usar um modelo de regressão, é
necessário garantir que ele seja estatisticamente significativo. Como
pode-se garantir isso? Bem, inicialmente, começamos analisando o resumo
do modelo por meio da função summary().
summary(linearMod)##
## Call:
## lm(formula = dist ~ speed, data = cars)
##
## Residuals:
## Min 1Q Median 3Q Max
## -29.069 -9.525 -2.272 9.215 43.201
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) -17.5791 6.7584 -2.601 0.0123 *
## speed 3.9324 0.4155 9.464 1.49e-12 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 15.38 on 48 degrees of freedom
## Multiple R-squared: 0.6511, Adjusted R-squared: 0.6438
## F-statistic: 89.57 on 1 and 48 DF, p-value: 1.49e-12
6.5.1. P-valor
O resumo estatístico do modelo apresentado acima nos diz várias coisas. Um deles é o p-valor do modelo (última linha inferior) e o p-valor de variáveis preditoras individuais (coluna da extrema direita em ‘Coeficientes’). Os p-valores, em geral, consideram um modelo linear como estatisticamente significativo apenas quando ambos os p-valores forem menores que o nível de significância estatística pré-determinado, que é idealmente 0,05. No R, isso é interpretado visualmente pelas “estrelas” no final da linha. Quanto mais estrelas ao lado do p-valor da variável, mais significativa a variável.
6.5.2. Hipótese Nula e Alternativa
Quando há um p-valor, há uma hipótese nula e uma hipótese alternativa associada a esse valor. No contexto de Regressão Linear, a Hipótese Nula é que os coeficientes associados às variáveis são iguais a zero, isto é, \(\beta_i = 0\). Por outro lado, a hipótese alternativa é que os coeficientes não são iguais a zero (ou seja, existe uma relação entre a variável independente em questão e a variável dependente).
6.5.3. Valor t
Em alternativa ao p-valor, a função summary também nos
traz o valor t baseado na distruição t de Student para avaliação das
hipóteses de teste, isto é, a significância ou não de um determinado
coeficiente. Neste contexto, então, observa-se que um valor t maior
indica que é menos provável que o coeficiente não seja igual a zero. Em
outras palavras, “quanto maior o valor t, melhor”.
6.5.4. Resíduos do Modelo linear
Os resíduos de um modelo linear são obtidos pela expressão \(r_i = y_i - \hat{y}_i\), em que \(y_i\) é o valor observado nos dados e \(\hat{y}_i\) é o valor da previsão. No R, em
vez trabalharmos com essa expressão, trabalhamos diretamente com os
métodos gráficos originados dessa expressão que podem ser obtidos de
duas formas: pela função plot(), ou pela função
autoplot() do pacote ggfortify
## Resíduos pela função plot():
par(mfrow = c(2, 3))
plot(linearMod, which = 1:6)## Resíduos pela função autoplot():
# Carregar o pacote:
library(ggfortify)## Warning: package 'ggfortify' was built under R version 4.2.2
# Uso da função autoplot():
# autoplot(object, ...)
autoplot(linearMod)Os gráficos de diagnóstico mostram os resíduos de quatro maneiras diferentes:
Residuals vs Fitted: Usado para verificar as suposições de relacionamento linear. Uma linha horizontal, sem padrões distintos é indício de uma relação linear, o que é bom.
Normal Q-Q: Usado para examinar se os resíduos são normalmente distribuídos. É bom que os pontos residuais sigam a linha tracejada reta.
Scale-Location (ou Spread-Location): Usado para verificar a homogeneidade da variância dos resíduos (homocedasticidade). A linha horizontal com pontos igualmente espalhados é uma boa indicação de homocedasticidade. Este não é o caso do nosso exemplo, onde temos um problema de heterocedasticidade.
Residuals vs Leverage: Usado para identificar casos influentes, ou seja, valores extremos que podem influenciar os resultados da regressão quando incluídos ou excluídos da análise. Este gráfico será descrito mais adiante nas próximas seções.
Observação: Os quatro gráficos mostram os 3 pontos de dados mais extremos rotulados com os números das linhas dos dados no conjunto de dados. Eles podem ser potencialmente problemáticos. Você pode querer examiná-los individualmente para verificar se há algo especial para o assunto ou se podem ser simplesmente erros de entrada de dados.
6.5.5. Coeficiente de Determinação \(R^2\)
Em geral, a informação real em um dado é a variação total que contém. Essa ideia, no contexto de Regressão Linear, é traduzida pelo valor de \(R^2\) que é chamado de coeficiente de determinação. O \(R^2\) nos diz a proporção da variação na variável dependente (resposta) que foi explicada pelo modelo linear. Matematicamente, essa medida é dada por:
\[R^2 = 1 − \dfrac{SSE}{SST}\] onde, SSE é a soma de quadrados dos erros descrita por:
\[SSE = \sum (y_i−\hat{y}_i)^2\]
e, SST é a soma de quadrados total descrita por:
\[SST = \sum (y_i−\bar{y})^2\]
Aqui, \(\hat{y}_i\) é o valor ajustado para a observação i e \(\bar{y}\) é a média de Y. Em geral, espera-se que esse valor seja próximo de 1 para que o modelo seja “eficaz”. Todavia, se isso não ocorre, não descartamos necessariamente um modelo baseado em um valor \(R^2\) baixo. Quando \(R^2\) é baixo, é necessário observar outras medidas como o AIC e a precisão da previsão do modelo ao decidir sobre a eficácia de um modelo.
6.5.6. Critérios AIC e BIC
No contexto de regressão linear, o critério de informação de Akaike - AIC (Akaike, 1974) e o critério de informação Bayesiano - BIC (Schwarz, 1978) são medidas da qualidade do ajuste de um modelo estatístico estimado, sendo utilizados principalmente para seleção de modelo. Ambos os critérios dependem da maximização da função de verossimilhança L do modelo estimado, e são, matematicamente, definidos por:
\[AIC = −2 \ln(L) + 2k\]
e,
\[BIC = -2\ln(L) + k \ln(n)\] em
que \(k\) é o número de parâmetros do
modelo, e \(n\) é o tamanho da amostra.
No R, para calcular essas duas medidas, fazemos o uso das funções
AIC e BIC, isto é,
## Uso da função AIC():
# AIC(object, ..., k = 2)
AIC(linearMod)## [1] 419.1569
## Uso da função BIC():
# BIC(object, ...)
BIC(linearMod)## [1] 424.8929
6.5.7. Critérios de Seleção
Selecionar modelos não é uma tarefa muito simples ou fácil, no entanto, existem algumas métricas que nos auxiliam nesse processo. As métricas mais comuns a serem observadas durante a seleção do modelo são:
| Estatística | Critério |
|---|---|
| \(R^2\) | Quanto maior, melhor (> 0,70) |
| Estatística F | Quanto maior, melhor |
| Erros-Padrão | Quanto mais próximo de zero, melhor |
| Estatística t | Deve ser maior que 1,96 ou menor que -1,96 |
| AIC | Quanto mais baixo melhor |
| BIC | Quanto mais baixo melhor |
| MAPE (Erro Percentual Médio Absoluto) | Quanto menor, melhor |
| MSE (Erro Quadrático Médio) | Quanto menor, melhor |
| Min_Max Accuracy = mean(min(actual, predicted)/max(actual, predicted)) | Quanto maior, melhor |
6.6. Predição de Modelo Linear
Até agora, vimos como construir um modelo de regressão linear usando todo o conjunto de dados. Se o construirmos dessa forma, não há como saber como o modelo se comportará com novos dados. Portanto, a prática preferida é dividir seu conjunto de dados em uma amostra 80:20 (treinamento:teste) e, em seguida, construir o modelo na amostra de 80% e usar o modelo assim construído para prever a variável dependente nos dados de teste.
Fazendo desta forma, teremos os valores preditos do modelo para os dados de 20% (teste) bem como os reais (do dataset original). Calculando as medidas de precisão (como min_max accuracy) e taxas de erro (MAPE ou MSE), podemos descobrir a precisão da previsão do modelo. Agora, vamos ver como realmente fazer isso.
- Etapa 1: criar amostras de dados de treinamento (desenvolvimento) e teste (validação) a partir dos dados originais.
## Criar base de dados para o treino
set.seed(100) # Definir uma semente para os resultados serem reprodutíveis
trainingRowIndex <- sample(1:nrow(cars), 0.8*nrow(cars)) # Índices das linhas para a base de dados de treino
trainingData <- cars[trainingRowIndex, ] # Base de dados de treino
head(trainingData)## speed dist
## 10 11 17
## 38 19 68
## 48 24 93
## 25 15 26
## 14 12 24
## 44 22 66
## Criar base de dados para o teste
testData <- cars[-trainingRowIndex, ] # Base de dados de teste
head(testData)## speed dist
## 3 7 4
## 5 8 16
## 17 13 34
## 24 15 20
## 28 16 40
## 32 18 42
- Etapa 2: Desenvolver o modelo nos dados de treinamento e usá-lo para prever a distância nos dados de teste.
## Criar o modelo linear para a base de treino
lmMod <- lm(dist ~ speed, data=trainingData)
## Fazer a predição
distPred <- predict(lmMod, testData)- Etapa 3: Revise as medidas de diagnóstico.
## Sumário do modelo
summary(lmMod)##
## Call:
## lm(formula = dist ~ speed, data = trainingData)
##
## Residuals:
## Min 1Q Median 3Q Max
## -24.726 -11.242 -2.564 10.436 40.565
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) -20.1796 7.8254 -2.579 0.0139 *
## speed 4.2582 0.4947 8.608 1.85e-10 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 15.49 on 38 degrees of freedom
## Multiple R-squared: 0.661, Adjusted R-squared: 0.6521
## F-statistic: 74.11 on 1 and 38 DF, p-value: 1.848e-10
## AIC e BIC
AIC (lmMod) ## [1] 336.6933
BIC (lmMod)## [1] 341.7599
- Etapa 4: Calcule a precisão da previsão e as taxas de erro.
Uma simples correlação entre os valores reais e previstos pode ser usada como uma forma de medida de precisão. Uma maior correlação na previsão implica que os valores reais e previstos tenham movimento direcional semelhante, ou seja, quando os valores reais aumentam, os valores previstos também aumentam e vice-versa.
## Previsões atuais
actuals_preds <- data.frame(cbind(actuals=testData$dist, predicteds=distPred))
## Correlação
correlation_accuracy <- cor(actuals_preds)
## Visualização das previsões
head(actuals_preds)## actuals predicteds
## 3 4 9.627845
## 5 16 13.886057
## 17 34 35.177120
## 24 20 43.693545
## 28 40 47.951757
## 32 42 56.468182
Agora, calculemos a medida Min_Max Accuracy e MAPE:
## Min_Max Accuracy:
min_max_accuracy <- mean(apply(actuals_preds, 1, min) / apply(actuals_preds, 1, max))
min_max_accuracy ## [1] 0.7311131
# MAPE = mean(abs(predicteds−actuals)actuals):
mape <- mean(abs((actuals_preds$predicteds - actuals_preds$actuals))/actuals_preds$actuals)
mape## [1] 0.4959096
7. Séries Temporais
7.1. Introdução
Uma série temporal (ST) é um conjunto de observações, geralmente equiespaçadas, obtidas a partir da medição/observação de uma variável ao longo do tempo. A frequência de observação/medição de uma ST pode variar dependendo do fenômeno observado: minuto, diária, semanal, mensal, anual etc. A partir da análise de ST, é possível obter subsídios para a escolha de um modelo adequado para modelar a série, escolhido dentro de uma classe de modelos pré-existentes. Uma vez construído, um modelo de ST pode ser utilizado para efetuar previsões probabilísticas sobre o futuro da série e/ou analisar a sensibilidade de algumas variáveis à variável objetivo. A capacidade de realizar previsões é fundamental no processo de tomada de decisões em diversos contextos e em diversos lugares como orgãos públicos e empresas.
Para trabalhar com séries temporais dentro do R, é necessário fazer o
uso da função ts(). O ts recebe a série, a
data de ínicio e a frequência. Por exemplo, podemos gerar uma série de
variáveis aleatórias da normal (um ruído branco) e transformar em série
temporal mensal começando em janeiro de 2000:
## Criar os dados
serie <- rnorm(100)
## Criar a série temporal
serie <- ts(serie, start = c(2000,01), freq = 12)
serie## Jan Feb Mar Apr May Jun
## 2000 1.75737562 -0.13792961 -0.11119350 -0.69001432 -0.22179423 0.18290768
## 2001 0.62286739 -0.52228335 1.32223096 -0.36344033 1.31906574 0.04377907
## 2002 0.98046414 -1.39882562 1.82487242 1.38129873 -0.83885188 -0.26199577
## 2003 0.20169159 -0.06991695 -0.09248988 0.44890327 -1.06435567 -1.16241932
## 2004 -2.07440475 0.89682227 -0.04999577 -1.34534931 -1.93121153 0.70958158
## 2005 1.42830143 -0.89295740 -1.15757124 -0.53029645 2.44568276 -0.83249580
## 2006 0.84287563 -1.45799372 -0.40030592 -0.77641729 -0.36929651 1.24010146
## 2007 0.12838606 1.01811999 -0.25557369 -0.30254101 1.61519068 -0.77371335
## 2008 1.00745738 -0.46956995 0.29789704 -0.41779443
## Jul Aug Sep Oct Nov Dec
## 2000 0.41732329 1.06540233 0.97020202 -0.10162924 1.40320349 -1.77677563
## 2001 -1.87865588 -0.44706218 -1.73859795 0.17886485 1.89746570 -2.27192549
## 2002 -0.06884403 -0.37888356 2.58195893 0.12983414 -0.71302498 0.63799424
## 2003 1.64852175 -2.06209602 0.01274972 -1.08752835 0.27053949 1.00845187
## 2004 -0.15790503 0.21636787 0.81736208 1.72717575 -0.10377029 -0.55712229
## 2005 0.41351985 -1.17868314 -1.17403476 -0.33292335 1.36311371 -0.46914734
## 2006 -0.10743381 0.17259351 0.25460127 -0.61453383 -1.42921510 -0.33097543
## 2007 0.42400240 -0.58394698 0.41503568 -1.54526166 -0.51874950 -0.27979155
## 2008
## Fazer o gráfico da série temporal
autoplot(serie)Séries temporais possuem três tipos de padrões, também chamados de componentes:
- Tendência: ocorre quando a variável da série temporal apresenta um aumento ou diminuição a longo prazo;
- Sazonalidade: corresponde a um padrão fixo que se repete no mesmo período de tempo (Ex.: aumento das vendas de roupa de praia no verão);
- Ciclos: ocorre quando os dados mostram subidas e quedas que não possuem um período fixo;
- Erro aleatório: diz respeito aos movimentos irregulares explicados por causas desconhecidas.
No geral, as séries Temporais podem exibir uma grande variedade de padrões que podem ser modelados separadamentes, o que pode ajudar o analista a entender melhor os dados e até mesmo a melhorar as previsões. Por exemplo, se assumirmos que a série segue um modelo aditivo, então, matematicamente, ela pode ser descrita pela equação \(y_t = S_t + T_t + E_t\), onde \(E_t\) é o componente do erro no período \(t\). Por outro lado, se a série for melhor descrita por um modelo multiplicativo, então a equação se torna \(y_t = S_t \cdot T_t \cdot E_t\). Assim, para se decidir se uma série segue um modelo aditivo ou multiplicativo, observa-se a magnitude dos períodos sazonais ou a variância da tendência cresce conforme o nível (valores absolutos) da série cresce.
7.2. Modelos de Média Móvel
Embora seja meio datada e tenha dado espaço para técnicas mais avançadas de decomposição, a média móvel é a base de muitos métodos de análises de séries temporais e uma importante etapa para estimar o componente de tendência de uma série. Na prática, os modelos de média móvel utilizam valores passados de erro de previsão de maneira semelhante a um modelo de regressão:
\[y_t = c + e_t + \theta_1 e_{t − 1} + \theta_2 e_{t − 2} + \ldots + \theta_q e_{t − q}\]
O modelo acima é chamado de modelo MA(q) e pode ser
interpretado como um modelo onde \(y_t\) é uma média ponderada dos erros de
previsão passados. No R, para trabalhar com os modelos de médias móveis,
podemos fazer o uso do pacote BETS por meio da função
ma(). Para exemplificar, vamos considerar a base de dados
energia disponível no pacote BETS.
## Carregar pacotes
library(BETS)## Warning: package 'BETS' was built under R version 4.2.2
## Registered S3 method overwritten by 'quantmod':
## method from
## as.zoo.data.frame zoo
## Registered S3 methods overwritten by 'forecast':
## method from
## autoplot.Arima ggfortify
## autoplot.acf ggfortify
## autoplot.ar ggfortify
## autoplot.bats ggfortify
## autoplot.decomposed.ts ggfortify
## autoplot.ets ggfortify
## autoplot.forecast ggfortify
## autoplot.stl ggfortify
## autoplot.ts ggfortify
## fitted.ar ggfortify
## fortify.ts ggfortify
## residuals.ar ggfortify
##
## Attaching package: 'BETS'
## The following object is masked from 'package:stats':
##
## predict
library(forecast)## Warning: package 'forecast' was built under R version 4.2.2
##
## Attaching package: 'forecast'
## The following object is masked from 'package:LaplacesDemon':
##
## is.constant
library(tidyverse)
library(ggplot2)
## Buscar e baixar a série temporal (no caso, a industria com código 1404)
# energia <- BETSget(1404)
# Salvar os dados
# saveRDS(energia, "datasets/ts_energia.Rda")
## Carregar os dados
energ <- readRDS("datasets/ts_energia.Rda")
energia <- ts(energ$value)
## Visualizando a série temporal
autoplot(energia) +
labs(x = NULL, y = "Consumo (Gwh)")Com a série temporal em mãos, vamos aplicar, então, nosso modelo de
médias móveis usando a função ma() do pacote
BETS:
## Carregar pacotes
library(BETS)
library(forecast)
library(tidyverse)
## Carregar os dados
energ <- readRDS("datasets/ts_energia.Rda")
energia <- ts(energ$value, start = 1, end = 42, frequency = 12)
## Gráfico da série tempora contra uma media movel de ordem de 3 meses:
# Uso da função ma():
# ma(x, order, centre = TRUE)
plot(energia)
ma(energia, 3) %>% lines(col = "red", lwd = 1)
# Como a média movel de 3 meses nao foi suficiente, vamos aumentar o periodo para 12 e 24 meses:
ma(energia, 12) %>% lines(col = "blue", lwd = 2)
ma(energia, 24) %>% lines(col = "green", lwd = 3)Do gráfico, podemos concluir que a curva que apresenta menos flutuações sazonais é verde, referente à média móvel de 24 períodos. Mesmo assim, pode-se dizer que essa decomposição não foi satisfatória, devido a curva apresentar perturbações mesmo usando um período longo (24 meses) para sua estimação.
7.3. Modelo ARIMA
No estudo de séries temporais, a combinação entre os métodos de diferenciação e os modelos de autoregressão e média móvel resultam em um modelo ARIMA (AutoRegressive Integrated Moving Average model) não-sazonal, que pode ser descrito matematicamente como:
\[Y_t = c + \phi_1 Y_{t-1} + \ldots + \phi_p Y_{t-p} + \theta_1 + \theta_1 e_{t-1} + \ldots + \theta_q e_{t-q} + e_t\] Ond \(Y_t\) é a série diferenciada. A equação acima é o que descreve o modelo ARIMA(p, d, q), onde:
- p: é a ordem do modelo autoregressivo.
- d: é o grau de diferenciação.
- q: é a ordem do modelo de média móvel.
Em termos práticos, é naturalmente complicado selecionar valores
apropriada para cada um desses três parâmetros, no entanto, ao trabalhar
com o R, há uma vantagem. No R, podemos partir do pacote
forecast por meio da função auto.arima() que
faz a seleção desses valores automaticamente automaticamente. Nesse
sentido, para exemplificar o uso do modelo ARIMA, iremos trabalhar com a
série temporal AirPassengers que descreve o número de
passageiros contabilizados mensalmente ao longo do período de 1949 -
1960, e está disponível no R. Em séries temporais, em geral, para que os
valores dos termos “p”, “d” e “q” sejam encontrados, e o ajuste seja
viabilizado, deve-se recorrer às funções de autocorrelação e de
autocorrelação parcial da teoria de séries temporais. No R, podemos
trablhar com essas funções diretamente por meio da função
ggtsdisplay do pacote forecast, isto é,
## Carregar pacote
library(forecast)
## Carregar os dados
data(AirPassengers)
## Fazer os gráficos de autocorrelação
# Uso da função ggtsdisplay():
# ggtsdisplay(x, plot.type = c("partial", "histogram", "scatter", "spectrum"),
# points = TRUE, smooth = FALSE, lag.max, na.action = na.contiguous,
# theme = NULL, ...)
ggtsdisplay(AirPassengers)A partir dos gráficos:
- ACF - função de autocorrelação
- PACF - função de autocorrelação parcial
Para a determinação da ordem de autoregressão (p), do grau de diferenciação (d) e da componente de médias móveis (q), deve-se realizar a observação do comportamento dos “lags” manifestados por cada gráfico de autocorrelação (ACF e ÁCF). “Nos gráficos gerados, as linhas tracejadas azuis são os limites de significância ou intervalos de confiança; sempre que houver uma ultrapassagem, diz-se que há, ali, um lag com significância”.
Para que o número de diferenciações seja encontrado com maior
facilidade, as funções ndiffs() e nsdiffs()
podem ser empregadas. Ambas retornam a quantidade de diferenciações
necessárias para a estabilização da estacionariedade e da variânica da
série. No entanto, a determinação dos valores dos termos pode não ser
exata, uma vez que o autor da análise pode incorrer em diversos erros e
gerar diversos tipos de modelos. Devido a isso, é necessária a
utilizaçaõ de uma métrica com o o intuito de se aferir a qualidade do
modelo gerado. Usualmente, se utiliza os valors de AIC e BIC, por
exemplo, gerados após o ajuste do modelo. Entendendo essa dificuldade,
dentre as ferramentas disponibilizadas pelo pacote
forecast, está a função auto.arima() que
realiza a verificação dos possíveis modelos gerados a partir da série
temporal em questão, visando ao ajuste ideal. Voltando, então, a nossa
série temporal AirPassengers, tem-se que:
## Modelos de treino e teste
modelo_treino <- ts(AirPassengers[1:120]) # Amostra treino
modelo_teste <- ts(AirPassengers[121:144]) # Amostra teste
# Modelo ajustado
modelo <- auto.arima(modelo_treino, stepwise = FALSE) # Criação do modelo
modelo## Series: modelo_treino
## ARIMA(2,1,2) with drift
##
## Coefficients:
## ar1 ar2 ma1 ma2 drift
## 1.6431 -0.9066 -1.8743 0.9688 2.1761
## s.e. 0.0371 0.0361 0.0572 0.0595 0.7107
##
## sigma^2 = 488.2: log likelihood = -537.51
## AIC=1087.03 AICc=1087.78 BIC=1103.7
Como pode-se perceber, o modelo ajustado foi um ARIMA, devido à ausência de componentes sazonais. Agora, suponha que nosso objetivo seja realizar a previsão para os próximos 24 períodos, o que permitirá a visualização da acurácia do modelo para a realização de previsões. Neste caso,
## Previsão
previsao <- forecast(modelo, h = 24)
## Leitura da previsão
head(previsao$lower) # Limite inferior## Time Series:
## Start = 121
## End = 126
## Frequency = 1
## 80% 95%
## 121 314.4380 299.4384
## 122 324.9450 306.0139
## 123 347.5203 327.4080
## 124 372.3897 352.1691
## 125 391.5983 371.3388
## 126 400.8089 380.3839
head(previsao$upper) # Limite superior## Time Series:
## Start = 121
## End = 126
## Frequency = 1
## 80% 95%
## 121 371.1076 386.1071
## 122 396.4682 415.3992
## 123 423.5063 443.6186
## 124 448.7850 469.0056
## 125 468.1406 488.4001
## 126 477.9761 498.4010
## Gráfico da previsão
autoplot(previsao, predict.colour = "red") +
labs(x = "Tempo", y = "Passageiros", title = "Previsao usando o forecast") +
theme_test() + scale_x_continuous(breaks = seq(0,150,15)) +
scale_y_continuous(breaks = seq(100, 600, 50)) + geom_vline(xintercept = 120, lty = "dashed")## Scale for x is already present.
## Adding another scale for x, which will replace the existing scale.
Verificação da acurácia da previsão com a amostra teste:
accuracy(modelo_teste, as.numeric(previsao$mean))## ME RMSE MAE MPE MAPE
## Test set -37.34932 67.58919 46.91646 -8.559127 10.94617
De acordo com a acurácia, podemos perceber que o modelo arima não é infalível, mas dependendo da qualidade do ajuste de dados, pode ser uma ferramenta poderosa para a análise de séries temporais. Cabe ressaltar que há outras ferramentas com as quais se pode realizar previsões, como as redes neurais, por exemplo.
7.4. Modelo de Cointegração
No geral, a desvantagem de utilizarmos modelos ARIMA para modelar séries temporais (especificamente, as econômicas) está na perda de informações importantes, principalmente, as de longo prazo. Isso por que ao diferenciair uma série para estacionarizá-la, muitas características se perdem (a constante, por exemplo). Uma solução para esse problema está no conceito de séries cointegradas ou, simplesmente, cointegração, que integra relaçãos de curto prazo com equilíbrios de longo prazo que podem ser estimados em separado ou conjuntamente.
Um conceito mais formal sobre cointegração foi proposto por Engle e Granger (1987) e nos possibilita trabalhar com regressões que envolvem variáveis de processos integrados de ordem um (I(1)) e as torna significativas. De acordo com a definição de Engle e Granger (1987), tem-se que:
Os elementos do vetor \(X_t = (x_1, x_2, \ldots, x_n)'\) são ditos cointegrados de ordem (d,b), denotados por \(X_t\sim CI(d,b)\), se:
Todos elementos \(X_t\) são integrados de ordem d, ou seja, são I(d).
Existe um vetor não-nulo, \(\beta\), tal que \(\alpha_t = X_t^{'}\beta \sim I(d-b), b > 0\). O vetor \(\beta\) é chamado de vetor de cointegração.
Para exemplificar esse conceito, no R, iremos aplicar a teoria da cointegração para definir uma estratégia de operação na bolsa de valores. A ideia é simples: se duas séries tem dependência de longo prazo e, no curto prazo uma série temporal se descola da outra, espera-se que, após algum tempo, haverá uma reversão para a outra série temporal. Para alcançar nosso objetivo, as seguintes atividades serão desenvolvidas:
Vamos trabalhar com as séries de ações de dois dos principais bancos privados brasileiros: Bradesco e Itaú.
Antes de iniciar nossa estratégia de operação, vamos fazer uma análise exploratória dessas duas séries temporais;
Criar uma estratégia de curto prazo para operar as ações;
Long-Short através de Cointegração: um exemplo usando o pacote PairTrading.
Primeiramente, vamos consultar o valor das ações desses dois bancos e observa-las graficamente.
## Carregar o pacote quantmod para pegar os valores de ações
require(quantmod)## Loading required package: quantmod
## Warning: package 'quantmod' was built under R version 4.2.2
## Loading required package: TTR
## Base de dados: Banco Bradesco S.A.
getSymbols('BBDC4.SA',src='yahoo')## [1] "BBDC4.SA"
## Agora, fazemos o gráfico da série temporal
chartSeries(BBDC4.SA)## Base de dados: Itaú S.A. - Investimentos Itaú S.A.
getSymbols('ITUB4.SA',src='yahoo')## [1] "ITUB4.SA"
## Agora, fazemos o gráfico da série temporal
chartSeries(ITUB4.SA)Agora que temos ambas as séries gerais, podemos estar interessados, por exemplo, em um período específico da série. Vamos supor que esse período seja relacionado ao últimos 4 meses. Então,
## Bradesco
chartSeries(BBDC4.SA,subset = 'last 4 months')## Itaú
chartSeries(ITUB4.SA,subset = 'last 4 months')Em geral, as estratégias de arbitragem estatística são baseadas em encontrar uma série temporal que possua a característica de estacionariedade ou reversão à média. Isto significa que é possível identificar situações em que a série divergiu de seu comportamento histórico e prever com alguma segurança que a série convergirá ou reverterá para um comportamento “médio”. O conceito de cointegração formaliza matematicamente este comportamento e permite a realização de testes estatísticos para detectar séries com este comportamento.
No contexto de operações com pares de ativos (pairs trading), a existência de uma relação de cointegração entre as séries de preços de dois ativos significa que pode ser possível realizar operações lucrativas de arbitragem. Por outro lado, se o par não for cointegrado, será impossível encontrar uma relação consistente para operar o par.
É necessário termos um teste para identificar quais pares de ações são cointegrados. Mesmo dentro do universos dos pares que são cointegrados, não há garantia de sucesso. É preciso que o par possua algumas características específicas para que uma estratégia de arbitragem seja consistentemente lucrativa:
- Relação de cointegração estável ao longo do tempo
- Reversão frequente do spread à média
- Variabilidade razoavelmente grande nas divergências
Neste contexto, quais são os passos para operar com pares de ações? Neste caso, os seguintes passos devem ser seguidos:
Selecionar duas ações que movem similarmente;
Vender as ações com preço elevado e comprar as ações com preço baixo;
Monitorar as diferenças entre as duas ações (isso no curto prazo);
No longo prazo, usar a teoria da cointegração para fazer esse monitoramento;
Regra de Decisão com base no Spread:
\[\text{Spread} = \log(Y_t) - (\alpha + \beta\log(X_t))\]
onde \(Y_t\) e \(X_t\) são ações. Logo, se Spread for muito alto, compra-se \(X_t\), vende-se \(Y_t\); todavia, se Spread for muito baixo, compra-se \(Y_t\), vende-se \(X_t\).
No R, para, então, estimar o Spread iremos fazer o
uso do pacote PairTrading. No entanto, esse pacote não está
mais disponível nas versões mais novas do R, dessa forma, para
trabalharmos com o pacote iremos utilizar o pacote que está disponível
no Github. Utilizando este pacote vamos estimar o
Spread entra as ações do Itaú e do Bradesco. A primeira
coisa que precisamos fazer é estimar a regressão linear abaixo:
## Instalar e carregar os pacotes
# install.packages("devtools")
library(devtools)## Warning: package 'devtools' was built under R version 4.2.2
## Loading required package: usethis
# install_github("cran/PairTrading")
library(PairTrading)## Loading required package: tseries
##
## Attaching package: 'tseries'
## The following object is masked from 'package:LaplacesDemon':
##
## read.matrix
## Organizar os dados
BBDC4.SA_2013 <- BBDC4.SA['2013::']
ITUB4.SA_2013 <- ITUB4.SA['2013::']
pairs <- cbind(BBDC4.SA_2013$BBDC4.SA.High,ITUB4.SA_2013$ITUB4.SA.High)
## Estimando o Spread
reg <- EstimateParameters(pairs, method = lm)Agora, para rodar o back-test nós precisamos estimar os
parâmetros historicamente de acordo com um determinado período. Este
processo pode ser feito por meio da função
EstimateParametersHistorically. Esse função faz algo como
uma “rolling regression” para estimar os parâmetros.
Assim,
## Estimativas dos parâmetros para back-test
params <- EstimateParametersHistorically(pairs, period = 180)Agora, nós vamos criar o sinal para a operação usando o
Spread. A função Simple nos dá uma
estratégia de operação bem simples: se o Spread é maior
(menor) que um valor específico, nós iremos comprar (vender). Neste
nosso caso, vamos fixar, por exemplo, nosso “valor específico” em 0.05,
então, baseado na nossa regressão:
\[\log(ITAU_t) = \alpha + \beta \cdot \log(BRAD_t) + u_t\]
onde o Spread é igual ao erro \((u_t = spread)\):
\[spread_t = \log(ITAU_t) − (\alpha + \beta \log(BRAD_t))\]
Então, iremos comprar Bradesco, se o spread for maior que 0.05 e comprar Itaú, se o spread for menor que -0.05. O gráfico abaixo mostra os pontos em que devemos comprar uma ou outra ação em que: “manchas roxas” na margem inferior (spread maior que 0.05), então compra-se Itaú; por outro lado, se “manchas roxas” na margem superior (spread menor que -0.05), compra-se Bradesco.
## Criando os sinais
signal <- Simple(params$spread, 0.05)
## Gráfico dos sinais
plot(params$spread, ylim = c(-0.3, 0.3));par(new=TRUE)
barplot(signal, space = 0, border=yarrr::transparent("blue", trans.val = .95),
col = yarrr::transparent("blue", trans.val = .95), xaxt = "n",
yaxt = "n", xlab = "", ylab = "")8. Processos de Otimização
A otimização, em geral, é um processo que usa um modelo matemático rigoroso para determinar a solução mais eficiente para um determinado problema. Para trabalhar com esse processo, primeiramente, devemos definir o objetivo que é uma medida quantitativa do desempenho (por exemplo, lucro, tempo, custo, energia potencial). Em geral, o objetivo pode ser definido como qualquer quantidade (ou combinação delas) representada como um único número.
Os problemas de otimização são necessariamente classificados em três grupos:
- Programação linear (PL): função objetivo e restrições são lineares.
- Programação quadrática (PQ): função objetivo é quadrática, mas as restrições são lineares.
- Programação não-linear (NLP): função objetivo ou pelo menos uma das restrições é não-linear.
No entanto, a estrutura básica de argumentos de um otimizador é sempre a mesma, e é dada por:
# optimizer(objective, constraints, bounds=NULL, types=NULL, maximum=FALSE)No R, os pacotes mais comuns para otimização são:
| Tipo de problema | Pacote | Rotina |
|---|---|---|
| General purpose (1-dim.) | Built-in | optimize(…) |
| General purpose (n-dim.) | Built-in | optim(…) |
| Linear Programming | lpSolve | lp(…) |
| Quadratic Programming | quadprog | solve.QP(…) |
| Non-Linear Programming | optimize | optimize(…) |
| optimx | optimx(…) | |
| General interface ROI | ROI | ROI_solve(…) |
Por exemplo, considere a função abaixo e suponha que nosso objetivo seja encontrar o ponto de mínimo.
f <- function(x)
{
2 * (x[1] - 1)^2 + 5 * (x[2] - 3)^2 + 10
}Neste caso, como otimizador, podemos trabalhar com o optim que tem por objetivo realizar processos de minimização, isto é:
r <- optim(c(1, 1), f)Naturalmente, em qualque processo de otimização, devemos checar se o
processo de otimização convergiu para o mínimo. No caso do optim, basta
resgatar o argumento convergence e comparar com 0 (pois é
um processo de minimização). Se o mesmo nos retorna como resposta
TRUE, então nosso processo de otimização convergiu.
r$convergence == 0 ## [1] TRUE
Por fim, verificamos o valor da função objetivo no ponto de mínimo encontrado que é retornado pelo argumento value do optim, isto é:
r$value## [1] 10
8.1. Otimizador Linear
No caso de otimização linear, vamos considerar um sistema linear, cujo objetivo é minimização, descrito na seguinte forma: \[\min_\mathbf{x}(c^T \mathbf{x}) = \min_\mathbf{x}(c_1 x_1 + ... + c_n x_n)\] sujeito as restrições: \(Ax \geqslant b\), \(x \geqslant 0\). Para facilitar nosso trabalhar, podemos reescrever esse sistema linear, em forma matricial, como:
\[\min_\mathbf{x} \left[\begin{matrix} c_1 \\ c_2 \\ \ldots \\ c_n\end{matrix}\right]^T\left[\begin{matrix} x_1 \\ x_2 \\ \ldots \\ x_n\end{matrix}\right]\] sujeito a: \[\left[\begin{matrix} a_{11} & a_{12} & \ldots & a_{1n} \\ a_{21} & a_{22} & \ldots & a_{2n}\\ \ldots & \ldots & \ldots & \ldots \\ a_{m1} & a_{m2} & \ldots a_{mn}\end{matrix}\right]\left[\begin{matrix} x_1 \\ x_2 \\ \ldots \\ x_n\end{matrix}\right]\geqslant \left[\begin{matrix} b_1 \\ b_2 \\ \ldots \\ b_n\end{matrix}\right]; \left[\begin{matrix} x_1 \\ x_2 \\ \ldots \\ x_n\end{matrix}\right] \geqslant 0\]
Para exemplificar, vamos considerar o seguinte problema de programação linear (PL):
- Função objetivo:
- O objetivo é maximizar o lucro total.
- Os produtos A e B são vendidos \(\$25\) e \(\$20\), respectivamente.
- Restrições de recursos:
- O produto A requer 20 unidades de recursos, o produto B precisa de 12.
- Apenas 1800 unidades de recursos estão disponíveis por dia.
- Restrições de tempo
- Ambos os produtos requerem um tempo de produção de 1/15 hora.
- Um dia de trabalho tem um total de 8 horas.
Pergunta: Qual é a solução ótima desse problema?
Solução: Neste caso, seja \(x_1\) o número de itens produzidos do produto A e \(x_2\) do produto B. A função objetivo, neste caso, maximiza o total de vendas, e é descrita como:
\[\text{Vendas}_{\max} = \max_{x_1,x_2} 25 x_1 + 20 x_2 = \max_{x_1,x_2} \left[\begin{matrix}25 \\ 20\end{matrix}\right]^T\left[\begin{matrix}x_1 \\ x_2\end{matrix}\right]\] sujeita as restrições: \(20 x_1 + 12 x_2 \leqslant 1800\), e \(\dfrac{1}{15} x_1 + \dfrac{1}{15} x_2 \leqslant 8\).
Para resolver esse problema, então, podemos trabalhar com o otimizador do pacote “lpSolve”. Isto é,
library(lpSolve)
# Objetivo
objective.in <- c(25, 20)
# Restrições
const.mat <- matrix(c(20, 12, 1/15, 1/15), nrow = 2, byrow = TRUE)
const.rhs <- c(1800, 8)
const.dir <- c("<=", "<=")
# Otimizador
optimum <- lp(direction="max", objective.in, const.mat,const.dir, const.rhs)Agora, com base no otimizador, temos que os valores ótimos de \(x_1\) e \(x_2\) desse problema de PL e o valor da função objetivo nos pontos ótimos são dados por:
## Valores ótimos para x_1 e x_2
optimum$solution## [1] 45 75
## Valor da função objetivo no ponto ótimo
optimum$objval## [1] 2625
8.2. Método Simplex
O método simplex é uma abordagem para resolver manualmente modelos de programação linear usando variáveis de folga, tableaus e variáveis pivô como meio de encontrar a solução ótima de um problema de otimização. Em poucas palavras, podemos resumir um problema de programação linear (PL) como um método para alcançar o melhor resultado dado uma equação máxima ou mínima com restrições lineares. A maioria dos problemas de PL pode ser resolvida usando um software como, por exemplo, o MatLab, mas o método simplex é uma técnica para resolver tais problemas manualmente. Sendo assim, para aplicar o método simplex, precisamos necessariamente dos seguintes passos:
- Estabelecer a forma padrão do problema de PL;
- Apresentando variáveis de folga;
- Criando o tableau;
- Apresentar as variáveis pivô;
- Criar um novo tableau;
- Verificar se o problema está otimizado;
- Identificar os valores ótimos;
Então, para exemplificar a apresentação desse método, vamos considerar o seguinte problema de PL:
- Objetivo: Minimizar: \(-\zeta = -8 x_1 - 10 x_2 - 7 x_3\)
- Restrições:
- \(x_1 + 3x_2 + 2x_3\leqslant 10\)
- \(-x_1 - 5x_2 - x_3 \geqslant - 8\)
- \(x_1, x_2, x_3 \geqslant 0\)
Vamos, então, trabalhar com os passos descritos anteriomente:
- 1º Passo: Definindo a forma padrão do problema de PL
A forma padrão é o formato ‘baseline’ para todos os problemas de PL antes de resolver o mesmo afim de encontrar a solução ótima e tem três requisitos:
- Deve ser um problema de maximização;
- Todas as restrições lineares devem estar em uma desigualdade menor que ou igual a;
- Todas as variáveis são não-negativas.
Esses requisitos sempre podem ser satisfeitos transformando qualquer problema de PL usando álgebra básica e substituição. A forma padrão é necessária porque cria um ponto de partida ideal para trabalhar com o método simplex da maneira mais eficiente possível, bem como outros métodos de resolução de problemas de otimização. Sendo assim, no nosso caso, para transformar um problema de PL de minimização em um problema de PL de maximização, basta multiplicar os lados esquerdo e direito da função objetivo por -1, isto é,
\[ (-1)\times (-z = -8 x_1 - 10 x_2 - 7 x_3) \rightarrow = z = 8x_1 + 10x_2 + 7x_3\] Agora, nosso objetivo se torna, necessariamente, um problema de PL de maximização. Por outro lado, a transformação de restrições lineares de uma desigualdade maior ou igual a uma desigualdade menor ou igual pode ser feita de forma semelhante ao que foi feito para a função objetivo. Ao multiplicar por -1 em ambos os lados, a desigualdade pode ser alterada para menor que ou igual a, isto é,
\[(-1) \times (-x_1 - 5x_2 - x_3 \geqslant - 8) \rightarrow x_1 + 5x_2 + x_3 \leqslant 8\]
Uma vez que o modelo está na forma padrão, passamos então para o segundo passo do método simplex.
- 2º Passo: Introduzindo as variáveis de folga
As variáveis de folga são variáveis adicionais que são introduzidas nas restrições lineares para transformá-las de restrições de desigualdade em restrições de igualdade. Se o modelo estiver na forma padrão, as variáveis de folga sempre terão um coeficiente positivo +1. No nosso caso, temos duas restrições, logo teremos duas variáveis de folga, isto é,
\[x_1 + 2x_2 + 2x_3 + s_1 = 10 \\ x_1 + 5x_2 + x_3 + s_2 = 8 \\ x_1, x_2, x_3, s_1, s_2 \geqslant 0\] onde \(s_1, s_2\) são as variáveis de folga. Depois que as variáveis de folga são introduzidas, passamos para o terceiro passo que é a construção do tableau.
- 3º Passo: Criando o tableau
Um tableau simplex é usado para realizar operações de linha no modelo de programação linear, bem como para verificar a otimização de uma solução. O tableau consiste no coeficiente correspondente às variáveis de restrição linear e nos coeficientes da função objetivo. Para o nosso problema, no tableau abaixo, a linha superior em negrito do tableau indica o que cada coluna representa. As duas linhas a seguir representam os coeficientes das variáveis de restrição linear do modelo de programação linear, e a última linha representa os coeficientes das variáveis da função objetivo.
| \(x_1\) | \(x_2\) | \(x_3\) | \(s_1\) | \(s_2\) | \(\zeta\) | \(\beta\) |
| 1 | 3 | 2 | 1 | 0 | 0 | 10 |
| 1 | 5 | 1 | 0 | 1 | 0 | 8 |
| -8 | -10 | -7 | 0 | 0 | 1 | 0 |
- 4º Passo: Verificar se o problema de PL está otimizado
A solução ótima de um modelo de programação linear de maximização são os valores atribuídos às variáveis na função objetivo para fornecer o maior valor \(\zeta\). Todavia, podemos fazer essa verificação pelo tableau. Então, para verificar a otimização usando o tableau, todos os valores na última linha devem conter valores maiores ou iguais a zero. Se um valor for menor que zero, significa que a variável não atingiu seu valor ótimo. Conforme visto na tableau do nosso exemplo, existem três valores negativos na linha inferior, indicando que essa solução não é a ideal. Se um tableau não for ideal, a próxima etapa é identificar a variável pivô e criar, então, um novo tableau.
- 5º Passo: Identificando as variáveis pivô
A variável pivô é usada em operações de linha para identificar qual variável se tornará o valor unitário e é um fator chave na conversão do valor unitário. A variável pivô pode ser identificada observando a linha inferior do tableau e o indicador. Assumindo que a solução não é ótima, escolha o menor valor negativo na linha inferior; um dos valores que se encontram na coluna desse valor será a variável pivô. Para encontrar o indicador, divida os valores \(\beta\) das restrições lineares por seus valores correspondentes da coluna que contém a possível variável pivô. A interseção da linha com o menor indicador não negativo e o menor valor negativo na linha inferior se tornará a variável pivô.
Em outras palavras, no nosso exemplo, -10 é o menor negativo na última linha. Isso designará a coluna \(x_2\) para conter a variável pivô. Assim, aplicando as condições citadas acima (no caso, fazemos \(b/x_i\)) para o indicador, obtemos um valor de 10/3 para a primeira restrição e um valor de 8/5 para a segunda restrição. Devido a 8/5 ser o menor indicador não negativo, o valor do pivô estará na segunda linha e terá um valor de 5. Com a variável pivô identificada, passamos ao nosso próximo passo que é criar um novo tableau.
- 6º Passo: Criar um novo tableau
O novo tableau será usado para identificar uma nova possível solução ótima. Agora que a variável pivô foi identificada na 5º Passo, as operações de linha podem ser executadas para otimizar a variável pivô, mantendo o restante do tableau equivalente. Isto é,
- Para encontrar o valor de \(s_2\) na linha 1, fazemos:
- Novo valor do tableau = (valor negativo na antiga coluna pivô do tableau) * (valor na nova linha pivô do tableau) + (valor antigo do tableau) = \((-3) \times \frac{1}{5} + 0 = -\frac{3}{5}\);
- Para encontrar a variável \(x_1\) na linha 3, fazemos:
- Novo valor do tableau = (valor negativo na antiga coluna pivô do tableau) * (valor na nova linha pivô do tableau) + (valor antigo do tableau) = $10 + (-8) = -6
e assim por diante.Portanto, após realizar as operações, obtemos o novo tableau:
| \(x_1\) | \(x_2\) | \(x_3\) | \(s_1\) | \(s_2\) | \(\zeta\) | \(\beta\) |
| 2/5 | 0 | 7/5 | 1 | -3/5 | 0 | 26/5 |
| 1/5 | 1 | 1/5 | 0 | 1/5 | 0 | 8/5 |
| -6 | 0 | -5 | 0 | 2 | 1 | 16 |
Observe que ainda temos valores negativos na última linha, então devemos continuar nossa busca da solução ótima. Para isso, repetimos o processo para o novo pivô encontrado. Após todo esse processo, encontramos com tableu final o seguinte tableau:
| \(x_1\) | \(x_2\) | \(x_3\) | \(s_1\) | \(s_2\) | \(\zeta\) | \(\beta\) |
| 0 | -2 | 1 | 1 | -1 | 0 | 2 |
| 1 | 5 | 1 | 0 | 1 | 0 | 8 |
| 0 | 30 | 1 | 0 | 8 | 1 | 64 |
Agora não temos mais valores negativos na última linha, então passamos para o último passo que é identificar a solução ótima.
- 7º Passo: Identificar os valores ótimos
Uma vez que o tableau seja comprovado como ótimo, os valores ótimos podem ser identificados. Estes podem ser encontrados distinguindo as variáveis básicas e não-básicas. Uma variável básica é uma variável que tem um único valor 1 em sua coluna e o restante ser todo zero. Se uma variável não atender a esse critério, ela é considerada não-básica. Se uma variável não é básica, significa que a solução ótima dessa variável é zero. Se uma variável for básica, a linha que contém o valor 1 corresponderá ao valor \(\beta\). O valor \(\beta\) representará a solução ótima para a variável dada. No nosso exemplo, temos:
- Variáveis básicas: \(x_1, s_1, z\)
- Variáveis não-básicas: \(x_2, x_3, s_2\)
Assim, para a variável \(x_1\), o 1 é encontrado na segunda linha. Isso mostra que o valor ideal de \(x_1\) é encontrado na segunda linha dos valores \(\beta\), que é 8. Por outro lado, a variável \(s_1\) tem um valor 1 na primeira linha, mostrando que o valor ideal é 2 na coluna \(\beta\). Devido a \(s_1\) ser uma variável de folga, ela não está realmente incluída na solução ótima, pois a variável não está contida na função objetivo. Já a variável \(\zeta\) tem um 1 na última linha; isso mostra que o valor objetivo máximo será 64 da coluna \(\beta\). Logo, a solução final do nosso problema de PL para cada uma das variáveis é:
\[x_1 = 8; x_2 = 0; x_3 = 0; s_1 = 2; s_2 =0; \zeta = 64\] Portanto, o valor ótimo para o máximo é 64 que é encontrado no ponto (8, 0, 0) da função objetivo.
Em resumo, o método simplex é uma abordagem para determinar manualmente o valor ótimo de um programa linear, produzindo uma solução ótima para satisfazer as restrições dadas e encontrar um valor \(\zeta\) máximo ótimo. No entanto, este método pode ser bastante extensivo se trabalharmos de forma análitica. Sendo assim, uma saída é implenta-lo computacionalmente.
Exercício: Considere a implementação a seguir do método simplex em R. Substitua os valores @@@ pelos argumentos corretos da função, e verifique se o método está implementado corretamente com base nos resultados do exemplo abordado nesta seção.
# simplex3 <- function(obj, const, rhs)
# {
# obj: vetor de coeficientes da função objetivo
# const: matriz de coeficientes de equação de restrição, na forma padrão (problema de maximização)
# rhs: vetor de valores correspondentes do lado direito para restrições. Observe que todas as restrições são "menores ou iguais a"
# co <- rbind(@@@, @@@)
# dims <- dim(@@@)
# numRow <- dims[@@@]
# M <- @@@
# tableau <- @@@
# print(@@@)
#
# dims <- dim(@@@)
# numCol <- dims[@@@]
# negIndicator <- @@@
#
# if(min(tableau[numRow,])>=0)
# {
# print("No negative indicators: algorithm terminates")
# negIndicator=FALSE
# }
#
# while(@@@)
# {
# pColIndex<-which.min(tableau[numRow,])
# pCol<-tableau[@@@,@@@]
# if(@@@)
# {
# print("The problem is unbounded, no maximum is possible.")
# return(tableau)
# }
# possibleRows = which(@@@ > @@@)
# lst<-tableau[@@@,@@@]
# div<-lst/pCol[possibleRows]
# minPos<-which.min(@@@)
# pRowIndex<-possibleRows[minPos]
#
# pivot<-tableau[@@@, @@@]
# tableau[pRowIndex,]<- @@@ * tableau[pRowIndex,]
#
# for(rows in c(1:@@@))
# {
# if(rows != pRowIndex)
# {
# tableau[rows,]<-tableau[rows,] + -1*tableau[@@@,@@@]*tableau[pRowIndex,]
# }
# }
# print(@@@)
# if(min(tableau[numRow,])>=@@@)
# {
# negIndicator=@@@
# }
# }
# return(tableau)
#}8.3. Métodos Numéricos
Considere a seguinte função:
\[f(x) = \dfrac{\log(x)}{(1 + x)}\]
Observe que, se desejamos maximizar essa função em relação a x, não é possível fazer isso de forma analítica. Como procedemos então? Neste caso, podemos trabalhar com os otmizadores disponíveis no R ou, então, trabalhar com os métodos clássicos da matemática como o método da bissecção ou Newton-Rapshon. Mas, antes de ir mais a fundo a esses métodos, vamos verificar o comportamento gráfico de f(x).
x <- seq(1, 5, by = 0.0001)
f <- function(x)
{
log(x)/(1 + x)
}plot(x, f(x), type = 'l', lwd = 2, xlab = 'x', ylab = 'f(x)', ylim = c(0, 0.3))Baseado neste gráfico, pode-se notar que o máximo de f(x) é próximo de 3. Pensando, então no processo iterativo, podemos escolher \(x^{(0)} = 3.0\) como valor inicial (ou chute inicial). A partir desse valor, atualizamos nossa equação com o objetivo de obter uma estimativa que seja mais próxima do ponto de máximo. Naturalmente, se a nova estimativa não for o máximo, utilizaremos ela para encontrar uma nova estimativa, e repetimos esse processo até encontrar então o ponto de máximo. As atualizações das estimativas, em geral, são baseadas na raíz da derivada da função f que, neste caso, é dada por:
\[f'(x) = \dfrac{\left(1 + \dfrac{1}{x} - \log(x)\right)}{(1 + x)^2}\] que pode ser implementada em R como:
df <- function(x)
{
(1 + 1/x - log(x))/(1 + x)^2
}8.3.1. Método da Bissecção
O primeiro método que temos, então, para resolver nosso problema é o método da bissecção. Neste método, se f’ é contínua em \([a_0, b_0]\) e \(f'(a_0)f'(b_0) \leqslant 0\), então o Teorema do Valor Intermediário nos diz que existe \(x_k \in [a_0, b_0]\) tal que \(f'(x_k) = 0\), e, então, k é ponto ótimo que maximiza f. Naturalmente, para trabalhar com esse método, devemos dividir nosso intervalo em subintervalos \([a_0,b_0] \supset [a_1, b_1] \supset \ldots\) até encontrar o ponto \(x_k\). Comecemos, então, com \(x_0 = \dfrac{a_0 + b_0}{2}\) sendo um valor inicial. Então, as equações que atualizam o processo iterativo do método da bissecção são:
\[ [a_{t+1}, b_{t+1}] = \begin{cases}[a_t, x^{(t)}], \text{ se } f'(a_t)f'(x^{(t)}) \leqslant 0 \\ [x^{(t)}, b_t], \text{ se } f'(a_t)f'(x^{(t)}) > 0\end{cases}\] e,
\[x^{(t+1)} = \dfrac{1}{2} (a_{t+1} + b_{t+1})\] OBS: Se f tiver mais de uma raiz no intervalo inicial, é fácil ver que a bissecção encontrará uma delas, mas não encontrará as demais.
Voltemos, então, ao nosso exemplo inicial, o caso da função f. Para aplicar o método da bissecção nessa função, temos os seguintes parâmetros:
- a = valor inicial do extremo esquerdo
- b = valor inicial do extremo direito
- x = valor inicial
- itr = número de iterações do método
- f = função objetivo
- df = primeira derivada da função objetivo
Agora, defina como valores iniciais: \((a_0, b_0) = (1,5)\) e 40 iterações do método, isto é,
a <- 1
b <- 5
x <- a+(b-a)/2
itr <- 40Implementando o método, embora de uma maneira não eficiente, temos:
for (i in 1:itr)
{
if(df(a) * df(x) < 0)
{
b <- x
}
else
{
a <- x
}
x <- a+(b-a)/2
}Assim, como resultados, obtemos:
# Ponto ótimo x_k
x## [1] 3.591121
# Função objetivo calculada no ponto ótimo
f(x)## [1] 0.2784645
# Derivada calculada no ponto ótimo
df(x)## [1] -1.121895e-14
Graficamente, pelo método da bissecção, o ponto de
máximo desejado é representado por:
Na prática, não podemos permitir que o procedimento seja executado indefinidamente, por isso exigimos uma regra de parada, baseada em alguns critérios de convergência, para desencadear o fim da aproximação sucessiva. A cada iteração, a regra de parada deve ser verificada. Quando os critérios de convergência são atendidos, o novo \(x^{(t+1)}\) é tomado como solução. Há duas razões para parar: se o procedimento parece ter alcançado uma convergência satisfatória ou se parece improvável que o faça em breve. Sendo assim, duas regras de parada são importantes:
- Critério de convergência absoluta: \(\left|x^{(t+1)} - x^{(t)}\right| < \epsilon\)
- Critério de convergência relativo: \(\dfrac{\left|x^{(t+1)} - x^{(t)}\right|}{\left|x^{(t)}\right|} < \epsilon\)
onde \(\epsilon\) é uma constante de precisão. Na prática, a imprecisão numérica em um computador pode impedir a convergência. Para a maioria dos métodos de aproximação iterativa, é mais seguro adicionar uma pequena correção a uma aproximação anterior do que iniciar uma nova aproximação do zero. No caso do método da bissecção, ele se torna mais estável numericamente quando o ponto final atualizado é calculado como, digamos, \(a_{t+1} = a_t + \frac{b_t − a_t}{2}\) em vez de \(a_{t+1} = \frac{a_t + b_t }{2}\).
Em resumo, podemos concluir que o método da bissecção é um exemplo de método de bracketing, ou seja, um método que limita uma raiz dentro de uma sequência de intervalos aninhados de comprimento decrescente. A bissecção é uma abordagem bastante lenta: requer um número bastante grande de iterações para atingir a precisão desejada, em relação a outros métodos como, por exemplo, Newton-Raphson.
8.3.2. Método de Newton-Raphson
Um dos métodos iterativos mais rápidos para se encontrar uma raíz de uma dada função é o método de Newton, especialmente em aplicações univariadas. Suponha que g’ é continuamente diferenciável e que g’‘(x_k) é diferente de 0. Na iteração t, a abordagem aproxima g’(x_k) pela expansão linear em série de Taylor:
\[0 = g'(x_k) \approx g'(x^{(t)} + (x_k - x^{(t)})g''(x^{(t)})\]
Como g’ é aproximado por sua reta tangente em \(x^{(t)}\), parece sensato aproximar a raiz de g’ pela raiz da reta tangente. Assim, resolvendo a equação acima para \(x_k\), obtemos:
\[x_k = x^{(t)} - \dfrac{g'(x^{(t)})}{g''(x^{(t)})} = x^{(t)} + h^{(t)}\]
onde \(h^{(t)} = - \frac{g'(x^{(t)})}{g''(x^{(t)})}\) é chamado de incremento de Newton.
Naturalmente, a atualização do método de Newton é descrita pela expressão \(x^{(t+1)} = x^{(t)} + h^{(t)}\). No entanto, podemos também trabalhar com a atualização do método encontrando a solução da aproximação quadrática da série de Taylor para \(g(x_k)\) descrita por:
\[g(x^{(t)}) + (x_k - x^{(t)})g'(x^{(t)}) + (x_k - x^{(t)})^2 \dfrac{g''(x^{(t)})}{2}\]
Para exemplificar, entãom voltemos ao caso da função f descrita no método da bissecção. Para aplicar o método de Newton nessa função, consideramos os seguintes parâmetros:
- x = valor inicial
- itr = número de iterações do método
- f = função objetivo
- f.prime = primeira derivada da função objetivo
- f.2prime = segunda derivada da função objetivo
Agora, defina como valores iniciais: x = 3 e 40 iterações do método, isto é,
x <- 3
itr <- 40Implementando o método, embora de uma maneira não eficiente, temos:
f <- function(x)
{
log(x)/(1+x)
}
f.prime <- function(x)
{
(1+(1/x)-log(x))/((1+x)^2)
}
f.2prime <- function(x)
{
(-1/((x^2)+(x^3)))-2*(1+(1/x)-log(x))/((1+x)^3)
}
for(i in 1:itr)
{
x = x - f.prime(x)/f.2prime(x)
}Portanto, como resultados, obtemos:
# Ponto ótimo x_k
x## [1] 3.591121
# Função objetivo calculada no ponto ótimo
f(x)## [1] 0.2784645
# Derivada calculada no ponto ótimo
df(x)## [1] 1.053423e-17
Graficamente, pelo método de Newton, o ponto de máximo desejado é representado por:
8.4. Máxima Verossimilhança
Quando falamos de processos de otimização, pensamos sempre maximizar ou minimizar uma expressão. Em termos de probabilidade, não seria diferente. Nesta seção, introduziremos um dos métodos mais comuns para se obter os parâmetros de um modelo de probabilidade ajustado a uma base de dados que é o metodo da máxima verossimilhança.
O princípio da verossimilhança afirma que devemos escolher aquele valor do parâmetro desconhecido que maximiza a probabilidade de obter a amostra particular observada, ou seja, o valor que torna aquela amostra a mais provável. Matematicamente, dada uma amostra aleatória \((X_1, \ldots, X_n)\) de uma variável aleatória X, a função de verossimilhança é definida por:
\[L(\theta) = \prod_{i=1}^{n} f_\theta (x_i)\]
em que \(f_\theta\) é a função
densidade de probabilidade no caso contínuo, ou a função de
probabilidade no caso discreto. O estimador de máxima verossimilhança é
obtido a partir da maximização de \(L\)
ou, equivalentemente, \(\ell(\theta) =
\ln(L(\theta))\). Dentro do R, existem várias formas de se
maximizar a função de verossimilhança, sendo as mais populares
utilizando o otmizador optim(), o otimizador
maxLik do pacote maxLik, ou o otimizador
fitdist do pacote fitdistrplus. Os dois
últimos, em particular, são baseados, por padrão, no método de
Newton-Rapshon. Para exemplificar como funcionar tais métodos,
consideramos uma amostra aleatória de tamanho 50 gerada de uma
distribuição exponencial com parâmetro \(\lambda = 5\).
## Carregar os pacotes
library(maxLik)## Loading required package: miscTools
## Warning: package 'miscTools' was built under R version 4.2.2
##
## Please cite the 'maxLik' package as:
## Henningsen, Arne and Toomet, Ott (2011). maxLik: A package for maximum likelihood estimation in R. Computational Statistics 26(3), 443-458. DOI 10.1007/s00180-010-0217-1.
##
## If you have questions, suggestions, or comments regarding the 'maxLik' package, please use a forum or 'tracker' at maxLik's R-Forge site:
## https://r-forge.r-project.org/projects/maxlik/
library(fitdistrplus)## Gerar uma amostra aleatória de tamanho 50 da distribuição exponencial com parâmetro lambda = 5
x <- rexp(50, rate = 5)
## Definir a função densidade de probabilidade, e função acumulada (necessárias para o fitdistrplus)
dmyexp <- function(x,lambda)
{
lambda * exp(-lambda * x)
}
pmyexp <- function(q,lambda)
{
1 - exp(-lambda * q)
}
## Definir a função de verossimilhança
ll.exp <- function(lambda)
{
n <- length(x)
ll <- n*log(lambda) - lambda * sum(x)
return(ll)
}
## Encontrar os estimadores de máxima verossimilhança
# Uso do optim():
lambda0 <- 4.5
fit1 <- optim(par = lambda0, fn=ll.exp, control = list(fnscale = -1), method = 'Brent', lower = 0, upper = 1000)
fit1$par## [1] 7.642864
# Uso do maxLik():
lambda0 <- 4.5
fit2 <- maxLik(logLik = ll.exp, start = c(lambda = lambda0))
fit2## Maximum Likelihood estimation
## Newton-Raphson maximisation, 5 iterations
## Return code 1: gradient close to zero (gradtol)
## Log-Likelihood: 51.68862 (1 free parameter(s))
## Estimate(s): 7.642865
# Uso do fitdistrplus():
lambda0 <- 4.5
fit3 <- fitdist(data = x, distr = 'myexp', start = list(lambda = lambda0))
fit3## Fitting of the distribution ' myexp ' by maximum likelihood
## Parameters:
## estimate Std. Error
## lambda 7.642864 1.080864
Além das alternativas acima, podemos também trabalhar diretamente com o método do Escore de Fisher em vez de fazer o uso de pacotes para encontrar os estimadores de máxima verossimilhança. Tal método pode ser entendido como uma variante estatística do método de Newton-Raphson, sendo resultado da substituição da segunda derivada \(f''(\cdot)\) de um parâmetro \(\theta\) pelo seu valor esperado. Assim, a atualização do método é dada por:
\[\theta^{(i + 1)}=\theta^{(i)} - \dfrac{f'(\theta^{(i)})}{E[f''(\theta^{(i)})]}\] Neste contexto, para exemplificar, vamos implementar uma rotina em R para o método escore de Fisher baseando-se na distribuição de probabilidade contínua Weibull.
# Código 1: Implementação da função log-verossimilhança
logvero <- function(n, t, theta)
{
mu <- theta[1]
beta <- theta[2]
l <- n * log(beta) - n * beta * log(mu) + (beta - 1) * sum(log(t)) - 1/mu^beta * sum(t^beta)
return(l)
}
# Código 2: Implementação do vetor escore (vetor gradiente que contém as derivadas da log-verossimilhança).
G <- function(n, t,theta)
{
mu <- theta[1]
beta <- theta[2]
A <- t/mu
U <- matrix(nrow = 2)
U[1] <- -n * beta/mu + beta/mu * sum(A^beta)
U[2] <- n/beta - n * log(mu) + sum(log(t)) - sum(log(A) * A^beta)
return(U)
}
# Código 3: Implementação da matriz hessiana (matriz Jacobiana do vetor escore).
J <- function(n, t,theta)
{
mu <- theta[1]
beta <- theta[2]
A <- t/mu
H <- matrix(nrow = 2, ncol = 2)
H[1,1] <- n * beta/mu^2 - (beta/mu)^2 * sum(A^beta) - beta/mu^2 * sum(A^beta)
H[1,2] <- -n/mu + beta/mu * sum(A^beta * log(A)) + 1/mu * sum(A^beta)
H[2,1] <- H[1,2]
H[2,2] <- -n/beta^2 - sum(A^beta * (log(A))^2)
return(H)
}
# Código 4: Implementação do método de Newton-Raphson.
set.seed(12345)
n <- 1000 # Tamanho de amostra
mu_0 <- 1
beta_0 <- 0.5
theta_0 <- matrix(c(mu_0, beta_0), nrow = 2)
t <- rweibull(1000, shape = 2, scale = 1.5) # Gerar os dados
erro <- 0.0005
itr <- 1
while(itr < 100)
{
itr <- itr + 1
theta_1 <- theta_0 - solve(J(n, t,theta_0))%*%G(n, t,theta_0)
if (max(abs(G(n, t,theta_1) - G(n, t,theta_0))) < erro) break
theta_0 <- theta_1
cat(itr,theta_1, "\n")
}## 2 1.481969 0.9379444
## 3 1.033923 1.548405
## 4 1.496041 2.227046
## 5 1.463714 1.996796
## 6 1.46633 2.012774
## 7 1.466341 2.012867
# Conferindo o ajuste:
hist(t, prob=TRUE, ylim = c(0,1))
curve(dweibull(x, scale = theta_1[1], shape=theta_1[2]), add=TRUE) 9. Geração de Valores Aleatórios
Nas seções anteriores, trabalhamos um pouco com a construção de funções e os principais otimizadores dentro do ambiente R. Nosso foco agora será trabalhar com algo que é suma importância na Estatística Computacional: geração de valores aleatórios para modelos de probabilidade. A geração de números aleatórios nos permite, por exemplo, gerar dados aleatórios para realizar uma simulação de dados com propriedades conhecidas, por exemplo, provenientes de determinadas distribuições de probabilidade para avaliar o desempenho de um teste para detecção de outliers ou o grau com o que o afastamento da normalidade influencia o desempenho do teste t. Além disso, podemos também avaliar níveis níveis cobertura de intervalos de confiança ou grau de vício de estimadores, bem como acessar a qualidade de um método de classificação e a velocidade de processamento de certa tarefa.
Neste contexto, então, iremos trabalhar com dois métodos importantes para gerar dados aleatórios: o método da transformação inversa, e o método de rejeição (ou aceitação rejeição).Essencialmente, para trabalhar com esses métodos devemos levar em conta seis fatores fundamentais:
- Velocidade.
- Tempo de inicialização.
- Comprimento do código.
- Independência da máquina, portabilidade.
- Número de operações dentro da função.
- Simplificidade e legibilidade.
9.1. Transformação Inversa
A ideia do método de transformação inversa é simplesmente representar nossa “complexa” variável aleatória como resultado de uma função aplicada a um variável aleatória uniforme, que nós sabemos como gerar. Em termos matemáticos, ele é baseado no seguinte resultado:
Teorema: Seja F uma função de distribuição contínua em \(\mathbb{R}\) tal que sua inversa seja definida por \(F^{-1}(u) = \inf\{x: F(x) = u, 0 < u < 1\}\). Se a variável aleatória U segue uma distribuição uniforme, \(U(0,1)\), então \(F^{-1}(U)\) tem função de distribuição \(F\). Ainda, se uma variável aleatória X tem função de distribuição \(F\), então \(F(X)\sim U(0,1)\). A função inversa, \(F^{-1}\) obtida é chamada de função quantil.
Em termos de computação, nosso algoritmo para gerar valores aleatórios com base na transformação inversa é descrito como:
- 1º Passo: Gerar dados de uma distribuição uniforme, U(0,1).
- 2º Passo: Retorne \(X = F^{-1}(U)\).
No entanto, para alguns modelos de probabilidade, nem sempre é possível encontrar essa função inversa, no caso, a função quantil. O que fazemos então? Uma saída é, a partir dos otimizadores vistos anteriormente, encontrar a solução númerica. Para exemplificar esse processo, vamos trabalhar com a distribuição exponencial com parâmetro \(\lambda\) com a seguinte função densidade de probabilidade:
\[f(x; \lambda) = \lambda e^{-\lambda x}\] Note que, para essa distribuição, a função de distribuição F é descrita por \(F(x;\lambda) = 1 - e^{-\lambda x}\). Nosso foco aqui é, necessariamente encontrar a função inversa de F com base na distribuição uniforme que, neste caso, é dada por:
\[ F^{-1}(U) = -\dfrac{1}{\lambda}\log(1 - U)\]
Vamos supor agora que não fosse possível encontrar a forma analítica dessa função F da distribuição exponencial. Neste caso, então, precisariamos de uma solução numérica. Para isso, vamos inicialmente definir a expressão analítica da função densidade de probabilidade da distribuição exponencial no R:
my.dexp <- function(x, lambda)
{
lambda*exp(-lambda*x)
}Agora, precisamos da função de distribuição. Visto que estamos focados em solução numérica, vamos, então, implementar a solução númerica da função de distribuição que, neste caso, é descrita pela rotina:
my.pexp.numerical <- function(x, lambda)
{
my.int <- function(x, lambda)
{
integrate(my.dexp, lambda = lambda, lower = 0, upper = x)$value
}
sapply(x, FUN=my.int, lambda)
}Com a função de distribuição em mãos, vamos ao procedimento de
encontrar a função inversa de F, ou função quantil. Como dependemos de
uma solução numérica, vamos trabalhar com a função uniroot
que é uma ‘procedure’ baseada no método de Newton-Rapshon. Logo, temos
que:
my.qexp.numerical <- function(q, lambda)
{
f <- function(P, fixed)
{
lambda <- fixed$lambda
q <- fixed$q
criterion <- q - my.pexp.numerical(P, lambda)
return(criterion)
}
P <- numeric(length(q))
for(i in 1:length(q))
{
fixed <- list(lambda = lambda, q = q[i])
root.p <- uniroot(f, lower = 0, upper = 100, fixed = fixed)
P[i] <- root.p$root
}
return(P)
}Pergunta: Será que a solução numérica acima tem o
mesmo resultado que a solução analítica considerando o mesmo valor de
\(\lambda\)? Para responder essa
questão, vamos comparar a nossa implementação com a função
qexp do R que define a função quantil da distribuição
exponencial de forma analítica. Para nosso experimento, então, vamos
considerar o valor de \(q = 0.9\) e
\(\lambda = 2\):
qexp(0.9, 2)## [1] 1.151293
my.qexp.numerical(0.9, 2)## [1] 1.151299
Calculando a diferença entre as duas, obtemos que:
my.qexp.numerical(0.9, 2) - qexp(0.9, 2)## [1] 6.906585e-06
ou seja, a diferença entre os resultados é 0.000006 que é aproximadamente igual à 0. Portanto, nossa implementação da solução numérica para encontrar a inversa de F é aceitável.
9.1.1. Geração de Dados de uma Distribuição Exponencial
Vamos considerar a distribuição exponencial. Nosso foco aqui é gerar uma amostra aleatória, digamos, de tamanho \(n = 10000\) e parâmetro \(\lambda = 2\) com base nessa distribuição. A pergunta é: como fazemos isso? De acordo com o método da transformação inversa, seguimos os seguintes passos:
- 1º Passo: Gerar dados de uma distribuição uniforme, U(0,1).
- 2º Passo: Retorne \(X = F^{-1}(U)\).
Mas antes, vamos escrever a função quantil da distribuição
exponencial (aqui, podemos também utilizar a função qexp),
isto é,
my.qexp.analytic <- function(q, lambda)
{
(-log(1-q))/lambda
} Para gerar nossa amostra, então, precisamos gerar uma distribuição uniforme e aplicar a função quantil. Para isto, vamos criar a função ‘my.rexp.inverse’:
my.rexp.inverse <- function(N, lambda)
{
U <- runif(N, min=0, max=1)
rnd.values <- my.qexp.analytic(U, lambda)
return(rnd.values)
}Por fim, nos resta gerar nossa amostra e verificar se ela, de fato, corresponde a uma distribuição exponencial. Mas como fazemos isso? Bom, o primeiro passo é executar nossa função com \(n = 10000\) e \(\lambda = 2\). Como segundo passo, podemos fazer o histograma dos dados gerados e anexar a curva da distribuição exponencial com \(\lambda = 2\), ou seja,
set.seed(1212) # Semente para reprodução de resultados
amostra <- my.rexp.inverse(1000,2)
hist(amostra,
freq=FALSE,
breaks=20,
xlab="x",
main=NULL,
col="grey")
curve(dexp(x, rate=2), add=TRUE)
Com base no histograma, podemos ver que nossa amostra aletória gerada,
de fato, segue uma distribuição exponencial com \(\lambda = 2\). É claro que essa é uma
maneira simples e superficial de verificar se a amostra provém da
distribuição em questão, para uma verificação mais completa, devemos
trabalhar com os métodos e estimação, e verificação de vieses. Agora,
vamos supor que desejamos realizar a mesma tarefa, porém, com bse na
função quantil analítica. Como geramos nossa amostra, neste caso?
Neste caso, basta trocar a função quantil analítica na função ‘my.rexp.inverse’ pela função quantil numérica, isto é,
my.rexp.inverse2 <- function(N, lambda)
{
U <- runif(N, min=0, max=1)
rnd.values <- my.qexp.numerical(U, lambda)
return(rnd.values)
}Agora, para gerar os dados, procedemos da mesma maneira que no caso da função quantil analítica, isto é,
set.seed(1212) # Semente para reprodução de resultados
amostra <- my.rexp.inverse2(1000,2)
hist(amostra,
freq=FALSE,
breaks=20,
xlab="x",
main=NULL,
col="grey")
curve(dexp(x, rate=2), add=TRUE)Novamente, com base no histograma, podemos ver que nossa amostra
aletória gerada, de fato, segue uma distribuição exponencial com \(\lambda = 2\). Mas uma coisa que podemos
nos questionar é: qual dos métodos é mais rápido? analítico ou
numérico? Para responder essa questão, vamos trabalhar com a
função system.time() que nos traz o tempo de execução de
uma função. Para este experimento, vamos considerar uma amostra
aleatória de tamanho \(n = 100000\) com
parâmetro \(\lambda = 2\).
set.seed(1212) # Semente para reprodução de resultados
system.time(my.rexp.inverse(1000,2))## user system elapsed
## 0 0 0
set.seed(1212) # Semente para reprodução de resultados
system.time(my.rexp.inverse2(1000,2))## user system elapsed
## 0.11 0.02 0.85
Observe que o tempo de sistema, no primeiro caso, é menor do que 0.01 segundo, porém, no segundo caso, temos um tempo de sistema de 0.94 segundos. Esse resultado é esperado uma vez que uma solução numérica tem como base aproximações sucessivas e isso demanda muito tempo computacional. Em suma, utilizamos esse método apenas quando é possível encontrar a solução analítica da função quantil (ou inversa de F); caso isso não seja possível, então, podemos utilizar, por exemplo, um método de geração de dados conhecido por método de rejeição (ou aceitação-rejeição) em vez do método da transformação inversa.
9.2. Aceitação-Rejeição
A ideia do método de rejeição é gerar dados quando não se é possível encontrar a inversa \(F^{-1}\) da função de distribuição F ou quando \(F\) não possui uma expressão anlítica. Em termos matemáticos, este método é baseado no seguinte resultado:
Teorema: Seja \(X\) um vetor aleatório com densidade \(f\) em \(\mathbb{R}^d\), e seja \(U\) uma variável aleatória que segue uma distribuição uniforme no intervalo \([0,1]\), \(U\sim U(0,1)\). Então, \((X, kUf(X))\) tem distribuição uniforme em \(A=\{(x,u): x \in \mathbb{R}^d, 0 \leqslant u \leqslant kf(x)\}\), onde \(k > 0\) é uma constante arbitrária. Ainda, se \((X,U)\) é um vetor aleatório em \(\mathbb{R}^{d+1}\) com distribuição uniforme em A, então X tem densidade \(f\) em \(\mathbb{R}^d\).
Com base no resultado acima, podemos escrever nosso algoritmo para o método da rejeição como: Seja \(g\) uma densidade da qual sabemos como amostrar e para a qual podemos calcular facilmente \(g(x)\). Seja \(e(\cdot)\) um envelope, tendo a propriedade \(e(x) = \frac{g(x)}{\alpha} \geqslant f(x)\) para todo \(x\) para o qual \(f(x) > 0\) para uma dada constante \(\alpha \leqslant 1\), isto é:.
- 1º Passo: Gerar \(y\) como sendo uma ocorrência da variável aleatória representada por \(G\), isto é, \(Y\sim g\).
- 2º Passo: Gerar \(u\) como sendo uma ocorrência de uma uniforme padrão, isto é, \(U\sim U(0,1)\).
- 3º Passo: Se \(u \leqslant \dfrac{f(y)}{kg(y)}\) considerar que \(x = y\) é um valor da distribuição de probabilidade alvo cuja densidade é \(f\), caso contrário, descartar \(y\).
- 4º Passo: Repetir até atingir o número de valores desejado \(n\).
9.2.1. Gerando Dados de uma Distribução Normal Com Base na Distribuição Cauchy
Para exemplificar o método, vamos considerar a distribuição normal (ou gaussiana) padrão dada pela densidade \(f = \frac{1}{\sqrt{2\pi} }e^{-\frac{1}{2}x^2}\). Nosso objetivo é gerar uma amostra aleatória, digamos, de tamanho \(n = 10000\) para essa distribuição. Sendo assim, com base no método da rejeição, precisamos de uma distribuição que tenha núcleo proporcional a distribuição normal padrão e, na literatura, a distribuição com essa propriedade é a distribuição Cauchy padrão (que chamaremos de densidade \(g = \frac{1}{\pi}\left(1+x^2\right)^{-1}\)), então, podemos trabalhar com a razão entre a normal e a cauchy.
Para entendermos melhor essa questão, vamos avaliar graficamente ambas as curvas e também a razão entre elas. Neste gráfico, podemos perceber que as distribuições são proporcionais, porém a Cauchy padrão ainda não ‘cobre’ totalmente a normal padrão; no entanto, nosso foco é encontrar o valor de \(k\) tal que \(k \cdot g(x)\) ‘cubra’ \(f(x)\).
par(mfrow = c(1, 2))
# Gaussiana e Cauchy.
curve(dnorm(x), -4, 4, ylab = 'Density')
curve(dcauchy(x), add = TRUE, lty = 2)
legend("topright", legend = c("Normal", "Cauchy"), lty = c(1, 2), bty = "n", cex = 0.7)
# Razão entre elas.
curve(dnorm(x)/dcauchy(x), -4, 4, ylab = 'Normal/Cauchy')
abline(v = c(-1, 1), lty = 2)Matematicamente, a razão entre a normal padrão e a cauchy padrão nos gera a expressão \(\sqrt{\frac{2 \pi}{e}}\) quando \(x = 1\). Logo, para encontrar o valor de \(k\), definimos \(k\) como \(k = \sqrt{\frac{2 \pi}{e}}\). No R:
k <- sqrt(2 * pi/exp(1))
k## [1] 1.520347
Agora a nossa questão é: **será que a distribuição cauchy padrão com densidade \(g\) cobre a distribuição normal padrão com densidade \(f\) se tivermos \(k\cdot g(x)\). Para responder essa questão, vamos refazer nosso gráfico sob essa nova condição:
curve(k * dcauchy(x), -4, 4,lty = 2,ylim = c(0, k * dcauchy(0)),ylab = "Density")
curve(dnorm(x), add = TRUE)
legend("topright",legend = c("f(x)", "k g(x)"), lty = c(1, 2), bty = "n", cex = 0.7)
Para aplicar o método de rejeição para gerar nossos dados, então,
devemos seguir os passos:
- 1º Passo: Gerar \(y\) como sendo uma ocorrência da variável aleatória representada por \(G\), isto é, \(Y\sim g\).
- 2º Passo: Gerar \(u\) como sendo uma ocorrência de uma uniforme padrão, isto é, \(U\sim U(0,1)\).
- 3º Passo: Se \(u \leqslant \dfrac{f(y)}{kg(y)}\) considerar que \(x = y\) é um valor da distribuição de probabilidade alvo cuja densidade é \(f\), caso contrário, descartar \(y\).
- 4º Passo: Repetir até atingir o número de valores desejado \(n\).
Neste caso, como nos baseamos na distribuição Cauchy padrão, precisamos gerar dados para ela como primeiro passo. Como a função inversa (ou quantil) da distribuição de Cauchy padrão é dada por \(F^{-1} = \tan(\pi (1-u))\), podemos aplicar o método da transformação inversa, neste caso. Sendo assim, para fixar as ideias, vamos gerar um valor da distribuição de Cauchy padrão:
y <- rcauchy(n = 1)
y## [1] -0.9411749
Agora, vamos ao 2º passo. Neste caso, devemos gerar valores de distribuição uniforme padrão, isto é, \(U(0,1)\). Como geramos apenas um valor da Cauchy padrão, aqui também iremos gerar apenas um valor, isto é,
u <- runif(n = 1)
u## [1] 0.6649981
Já o 3º passo nos diz que se \(u \leqslant \dfrac{f(y)}{kg(y)}\), consideramos \(x = y\) e aceitamos o valor; do contrário, descartamos o valor. Isto é,
f <- function(x)
{
dnorm(x, 0, 1)
}
g <- function(x)
{
dcauchy(x, 0, 1)
}
r <- f(y)/(k * g(y))
r## [1] 0.9983067
if (u < r) {
x <- y
print("u < r, então valor aceito.")
} else {
print("u >= r, então valor descartado.")
}## [1] "u < r, então valor aceito."
Por fim, o último passo nos diz para repetir esse processo até atingir o tamanho de amostra desejado, ou seja,
set.seed(1212) # Semente para reprodução de resultados
n <- 1 # Contador de valores aceitos.
l <- 1 # Contador de ciclos.
N <- 100 # Total de número à gerar.
x <- numeric(N) # Vetor vazio
while (n < N)
{
y <- rcauchy(n = 1)
u <- runif(n = 1)
w <- f(y)/(k * g(y))
if (u < w) {
x[n] <- y
n <- n + 1
}
l <- l + 1
}Após esse processo, podemos calcular as taxas de aceitação, teórica e numérica, do algoritmo. Neste caso, tais taxas são dadas, em porcentagem, por
# Taxa de aceitação:
round(n/l * 100, 2) # Observada## [1] 72.46
round(1/k * 100, 2) # Teórica## [1] 65.77
Mas como sabemos se nossos valores gerados são, de fato, provenientes de uma distribuição normal padrão? Ora, para verificar essa questão, podemos trabalhar com o gráfico da função de distribuição (F) dos valores gerados e, em seguida, adicionar a curva normal em cima desse gráfico. Se a curva for aproximadamente igual, então nosso algoritmo de geração é aceitável, isto é, a distribuição de Cauchy padrão é uma ótima escolha para cobrir e gerar dados para a distribuição normal padrão.
plot(ecdf(x))
curve(pnorm(x), add = TRUE, col = 2)
Portanto, com base no gráfico da função de distribuição, vemos que nosso
algoritmo de geração de dados foi satisfatório.
10. Simulação Monte Carlo
10.1. Introdução
Nesta seção, abordaremos o conceito de simulação Monte Carlo, ou método de integração Monte Carlo. Este método, em geral, pode ser resumido como a estimativa estatística do valor de uma integral usando avaliações de um integrando em um conjunto de pontos escolhidos aleatoriamente de uma distribuição com suporte sobre o intervalo de integração. Essa estimativa via simulação Monte Carlo pode ser útil em uma ampla variedade da teoria de probabilidades. Por exemplo, nas análises Bayesianas, a média a posteriori pode ser escrita na forma de uma integral, mas normalmente não pode ser avaliada analiticamente; o cálculo do risco na teoria da decisão Bayesiana depende da integração; o víes e o erro-quadrático médio de uma estimativa de um parâmetro de uma distribuição de probabilidade também pode depender do uso de integrais; dentre muitas outras aplicações. Neste sentido, nosso foco será ilustrar, no R, como pode ser realizado o processo de simulação Monte Carlo.
10.2. O Método de Monte Carlo
No geral, muitas quantidades de interesse em análises estatísticas inferenciais podem ser expressas como a esperança de uma função de uma variável aleatória, digamos \(E\{h(X)\}\). Por exemplo, seja \(f\) a densidade da variável aleatória X, e \(\mu\) a esperança (média) de \(h(X)\) em relação a \(f\). Quando umaamostra aleatória (i.i.d.) \(X_1, \ldots, X_n\) é obtida de \(f\), podemos aproximar \(\mu\) pela média amostral da seguinte forma:
\[\hat \mu_{MC} = \dfrac{1}{n}\sum_{i=1}^{n} h(X_i) \rightarrow \int h(x)f(x) dx = \mu\]
em que \(n\rightarrow \infty\), pela lei dos grandes números. Além disso, seja \(v(x) = [h(x) − \mu]^2\), e assuma que \(h(X)^2\) tem esperança finita sob \(f\). Então a variância amostral de \(\hat \mu_{MC}\) é \(\sigma^2/n = E\{v(X)/n\}\), onde a esperança é tomada em relação a \(f\). Uma abordagem de Monte Carlo semelhante pode ser usada para estimar \(\sigma^2\). Tal abordagem é descrita por:
\[ \hat{var}\{\hat\mu_{MC}\}= \dfrac{1}{n-1} \sum_{i=1}^{n}\left[h(X_i) - \hat\mu_{MC}\right]^2\] Quando \(\sigma^2\) existe, o teorema do limite central implica que \(\mu_{MC}\) tem uma distribuição aproximadamente normal para \(n\) grandes. Em geral, a integração de Monte Carlo fornece convergência lenta de orderm \(O(n^{−1/2})\).
10.3. Viés e EQM
Nosso objetivo aqui é trabalhar como o conceito anterior sobre o método de Monte Carlo para avaliar o viés (V) e o erro quadrático médio (EQM) dos estimadores de máxima verossimilhança de um dado modelo de probabilidade. Neste contexto, considere uma amostra aleatória (i.i.d.) \((X_1, \ldots, X_n)\) de uma variável aleatória X cuja a função de verossimilhança é definida por:
\[L(\theta) = \prod_{i=1}^{n} f_\theta (x_i)\]
em que \(f_\theta\) é a função densidade de probabilidade, e \(\theta\) é o vetor de parâmetros. O estimador de máxima verossimilhança é obtido a partir da maximização de \(L\) ou, equivalentemente, \(\ell(\theta) = \ln(L(\theta))\), e é denotado por \(\hat\theta\). A partir desse estimador, nosso interesse, é utilizar o método de Monte Carlo para obter o V e EQM de \(\theta\) de acordo com as equações:
\[V(\widehat{\theta}) = \frac{1}{N} \sum_{i = 1}^{N}(\widehat{\theta}_{i} - \theta)\] \[EQM(\widehat{\theta}) = \frac{1}{N} \sum_{i = 1}^{N}(\widehat{\theta}_{i} - \theta)^{2}\] em que \(\theta\) é o vetor de parâmetros, e \(N\) é o número de simulações para o método de Monte Carlo.
10.4. Experimentos Monte Carlo
Para exemplificar o método de Monte Carlo para o cálculo do V e do EQM, iremos, nesta seção, considerar o modelo de probabilidade Weibull em alguns cenários práticos diferentes:
- Cenário 1: Variação apenas do número de simulações, com valores paramétricos e tamanhos amostrais fixos.
- Cenário 2: Variação o número de simulações, e os tamanhos amostrais, com valores paramétricos fixos.
- Cenário 3: Variação o número de simulações, os tamanhos amostrais, e os valores paramétricos.
10.4.1. Cenário 1
Para o nosso primeiro cenário, podemos realizar a simulação de duas formas: com o uso de laço de repetições, ou sem o uso de laço de repetições. Em ambos os casos, o resultado será o mesmo, mas a performance é diferente. Vamos começar com o primeiro caso, usando laço de repetição.
## Carregar os pacotes necessários
library(fitdistrplus)
## Criar uma matriz para armazenar os resultados
out <- matrix(ncol = 2, nrow = 1000)
## Estrutura da simulação
N <- 1000 # Número de simulações Monte Carlo
n <- 100 # Tamanho de amostra
par <- c(1.5, 2) # Parâmetros fixos
## Simulação
for(i in 1:N)
{
set.seed(123) # Semente utilizada para reprodução de resultados
dados <- rweibull(n = n, shape = par[1], scale = par[2])
aux <- fitdist(data = dados, distr = "weibull", start = list(shape = par[1], scale = par[2]))$estimate
out[i, ] <- aux
}
## Viés
V.1 <- mean(out[,1] - par[1], na.rm = TRUE) # Parâmetro 'shape'
V.1## [1] 0.03000923
V.2 <- mean(out[,2] - par[2], na.rm = TRUE) # Parâmetro 'scale'
V.2## [1] -0.001203025
## EQM
EQM.1 <- mean((out[,1] - par[1])^2, na.rm = TRUE) # Parâmetro 'shape'
EQM.1## [1] 0.000900554
EQM.2 <- mean((out[,2] - par[2])^2, na.rm = TRUE) # Parâmetro 'scale'
EQM.2## [1] 1.44727e-06
Para o segundo caso, sem uso de laço de repetições, temos:
## Carregar os pacotes necessários
library(fitdistrplus)
## Estrutura da simulação
N <- 1000 # Número de simulações Monte Carlo
n <- 100 # Tamanho de amostra
par <- c(1.5, 2) # Parâmetros fixos
## Simulação
set.seed(123) # Semente utilizada para reprodução de resultados
x <- rweibull(n * N, shape = par[1], scale = par[2]) # Gerar uma amostra grande de dados
dados <- data.frame(matrix(x, ncol = N))
emvs <- lapply(dados, fitdist, distr = "weibull", start = list(shape = par[1], scale = par[2])) # Estimativas
out <- do.call("rbind", lapply(emvs, "[[", "estimate")) # Resgatar as estimativas
## Viés
V.1 <- mean(out[,1] - par[1], na.rm = TRUE) # Parâmetro 'shape'
V.1## [1] 0.02645651
V.2 <- mean(out[,2] - par[2], na.rm = TRUE) # Parâmetro 'scale'
V.2## [1] 0.001423539
## EQM
EQM.1 <- mean((out[,1] - par[1])^2, na.rm = TRUE) # Parâmetro 'shape'
EQM.1## [1] 0.0150484
EQM.2 <- mean((out[,2] - par[2])^2, na.rm = TRUE) # Parâmetro 'scale'
EQM.2## [1] 0.01907509
10.4.1. Cenário 2
Para o nosso segundo cenário, diferentemente do primeiro cenário, não é possível “cortar” os laços de repetição, no entanto, podemos trabalhar com apenas um laço de repetição. Para melhor visualizar esse cenário, que agora trabalha com a variação do tamanho amostra, vamos considerar um experimento Monte Carlo com \(N = 10, 100, 1000\) simulações.
- Primeiro caso: 10 simulações:
## Carregar os pacotes necessários
library(fitdistrplus)
## Estrutura da simulação
set.seed(123) # Semente utilizada para reprodução de resultados
N <- 10 # Número de simulações Monte Carlo
n <- seq(20, 200, 30) # Tamanhos amostrais
par <- c(1.5, 2) # Parâmetros fixos
x <- rweibull(max(n) * N, shape = par[1], scale = par[2]) # Gerar um conjunto 'gigante' de dados
dados <- matrix(x, ncol = N) # Converter os dados gerados em uma matriz
entrada <- data.frame(dados) # Converter os dados gerados em um data.frame
est <- matrix(nrow = 7, ncol = 2) # Matriz para salvar as estimativas
k <- 1
## Simulação
for (amostra in n)
{
X <- entrada[1:amostra, ]
emvs <- lapply(X, fitdist, distr = "weibull", start = list(shape = par[1], scale = par[2]))
est[k, ] <- apply(do.call("rbind", lapply(emvs, "[[", "estimate")), 2, mean)
cat(paste('Tamanho de amostra:', amostra), "\n")
k <- k + 1
}## Tamanho de amostra: 20
## Tamanho de amostra: 50
## Tamanho de amostra: 80
## Tamanho de amostra: 110
## Tamanho de amostra: 140
## Tamanho de amostra: 170
## Tamanho de amostra: 200
## Viés
V <- est - matrix(par, nrow = 7, ncol = 2, byrow = T)
V[ ,1] # Parâmetro 'shape'## [1] 0.066818632 0.042037932 0.014662212 0.006603664 -0.010478278
## [6] 0.010820619 0.020569848
V[ ,2] # Parâmetro 'scale'## [1] -0.031637787 -0.038191886 -0.017591478 -0.028220655 -0.021895271
## [6] 0.007247391 0.009080085
# Gráficos
par(mfrow = c(1,2))
plot(n, V[ ,1], type = "b", ylab = 'Vies: shape', xlab = 'Tamanho de amostra', pch = 19)
abline(h = 0, col = 'red', lwd = 2)
plot(n, V[ ,2], type = "b", ylab = 'Vies: scale', xlab = 'Tamanho de amostra', pch = 19)
abline(h = 0, col = 'red', lwd = 2)## EQM
EQM <- (est - matrix(par, nrow = 7, ncol = 2, byrow = T))^2
EQM[ ,1] # Parâmetro 'shape'## [1] 4.464730e-03 1.767188e-03 2.149805e-04 4.360838e-05 1.097943e-04
## [6] 1.170858e-04 4.231186e-04
EQM[ ,2] # Parâmetro 'scale'## [1] 1.000950e-03 1.458620e-03 3.094601e-04 7.964054e-04 4.794029e-04
## [6] 5.252467e-05 8.244795e-05
# Gráficos
par(mfrow = c(1,2))
plot(n, EQM[ ,1], type = "b", ylab = 'EQM: shape', xlab = 'Tamanho de amostra', pch = 19)
abline(h = 0, col = 'red', lwd = 2)
plot(n, EQM[ ,2], type = "b", ylab = 'EQM: scale', xlab = 'Tamanho de amostra', pch = 19)
abline(h = 0, col = 'red', lwd = 2)- Segundo caso: 100 simulações:
## Carregar os pacotes necessários
library(fitdistrplus)
## Estrutura da simulação
set.seed(123) # Semente utilizada para reprodução de resultados
N <- 100 # Número de simulações Monte Carlo
n <- seq(20, 200, 30) # Tamanhos amostrais
par <- c(1.5, 2) # Parâmetros fixos
x <- rweibull(max(n) * N, shape = par[1], scale = par[2]) # Gerar um conjunto 'gigante' de dados
dados <- matrix(x, ncol = N) # Converter os dados gerados em uma matriz
entrada <- data.frame(dados) # Converter os dados gerados em um data.frame
est <- matrix(nrow = 7, ncol = 2) # Matriz para salvar as estimativas
k <- 1
## Simulação
for (amostra in n)
{
X <- entrada[1:amostra, ]
emvs <- lapply(X, fitdist, distr = "weibull", start = list(shape = par[1], scale = par[2]))
est[k, ] <- apply(do.call("rbind", lapply(emvs, "[[", "estimate")), 2, mean)
cat(paste('Tamanho de amostra:', amostra), "\n")
k <- k + 1
}## Tamanho de amostra: 20
## Tamanho de amostra: 50
## Tamanho de amostra: 80
## Tamanho de amostra: 110
## Tamanho de amostra: 140
## Tamanho de amostra: 170
## Tamanho de amostra: 200
## Viés
V <- est - matrix(par, nrow = 7, ncol = 2, byrow = T)
V[ ,1] # Parâmetro 'shape'## [1] 0.11079699 0.06280482 0.04201301 0.02856296 0.02508897 0.02427846 0.02453957
V[ ,2] # Parâmetro 'scale'## [1] -0.002206710 -0.005140790 0.015984249 0.005484858 0.007104302
## [6] 0.012600827 0.013083646
# Gráficos
par(mfrow = c(1,2))
plot(n, V[ ,1], type = "b", ylab = 'Vies: shape', xlab = 'Tamanho de amostra', pch = 19)
abline(h = 0, col = 'red', lwd = 2)
plot(n, V[ ,2], type = "b", ylab = 'Vies: scale', xlab = 'Tamanho de amostra', pch = 19)
abline(h = 0, col = 'red', lwd = 2)## EQM
EQM <- (est - matrix(par, nrow = 7, ncol = 2, byrow = T))^2
EQM[ ,1] # Parâmetro 'shape'## [1] 0.0122759722 0.0039444449 0.0017650928 0.0008158428 0.0006294566
## [6] 0.0005894437 0.0006021904
EQM[ ,2] # Parâmetro 'scale'## [1] 4.869570e-06 2.642773e-05 2.554962e-04 3.008367e-05 5.047111e-05
## [6] 1.587808e-04 1.711818e-04
# Gráficos
par(mfrow = c(1,2))
plot(n, EQM[ ,1], type = "b", ylab = 'EQM: shape', xlab = 'Tamanho de amostra', pch = 19)
abline(h = 0, col = 'red', lwd = 2)
plot(n, EQM[ ,2], type = "b", ylab = 'EQM: scale', xlab = 'Tamanho de amostra', pch = 19)
abline(h = 0, col = 'red', lwd = 2)- Terceiro caso: 1000 simulações:
## Carregar os pacotes necessários
library(fitdistrplus)
## Estrutura da simulação
set.seed(123) # Semente utilizada para reprodução de resultados
N <- 1000 # Número de simulações Monte Carlo
n <- seq(20, 200, 30) # Tamanhos amostrais
par <- c(1.5, 2) # Parâmetros fixos
x <- rweibull(max(n) * N, shape = par[1], scale = par[2]) # Gerar um conjunto 'gigante' de dados
dados <- matrix(x, ncol = N) # Converter os dados gerados em uma matriz
entrada <- data.frame(dados) # Converter os dados gerados em um data.frame
est <- matrix(nrow = 7, ncol = 2) # Matriz para salvar as estimativas
k <- 1
## Simulação
for (amostra in n)
{
X <- entrada[1:amostra, ]
emvs <- lapply(X, fitdist, distr = "weibull", start = list(shape = par[1], scale = par[2]))
est[k, ] <- apply(do.call("rbind", lapply(emvs, "[[", "estimate")), 2, mean)
cat(paste('Tamanho de amostra:', amostra), "\n")
k <- k + 1
}## Tamanho de amostra: 20
## Tamanho de amostra: 50
## Tamanho de amostra: 80
## Tamanho de amostra: 110
## Tamanho de amostra: 140
## Tamanho de amostra: 170
## Tamanho de amostra: 200
## Viés
V <- est - matrix(par, nrow = 7, ncol = 2, byrow = T)
V[ ,1] # Parâmetro 'shape'## [1] 0.09862877 0.03294364 0.02467645 0.01725981 0.01528471 0.01355049 0.01170997
V[ ,2] # Parâmetro 'scale'## [1] -0.013653828 -0.006704724 0.005292578 0.003561858 0.002797891
## [6] 0.001024363 0.001448506
# Gráficos
par(mfrow = c(1,2))
plot(n, V[ ,1], type = "b", ylab = 'Vies: shape', xlab = 'Tamanho de amostra', pch = 19)
abline(h = 0, col = 'red', lwd = 2)
plot(n, V[ ,2], type = "b", ylab = 'Vies: scale', xlab = 'Tamanho de amostra', pch = 19)
abline(h = 0, col = 'red', lwd = 2)## EQM
EQM <- (est - matrix(par, nrow = 7, ncol = 2, byrow = T))^2
EQM[ ,1] # Parâmetro 'shape'## [1] 0.0097276352 0.0010852835 0.0006089270 0.0002979010 0.0002336223
## [6] 0.0001836158 0.0001371235
EQM[ ,2] # Parâmetro 'scale'## [1] 1.864270e-04 4.495332e-05 2.801138e-05 1.268683e-05 7.828193e-06
## [6] 1.049320e-06 2.098170e-06
# Gráficos
par(mfrow = c(1,2))
plot(n, EQM[ ,1], type = "b", ylab = 'EQM: shape', xlab = 'Tamanho de amostra', pch = 19)
abline(h = 0, col = 'red', lwd = 2)
plot(n, EQM[ ,2], type = "b", ylab = 'EQM: scale', xlab = 'Tamanho de amostra', pch = 19)
abline(h = 0, col = 'red', lwd = 2)10.4.3. Cenário 3
Para finalizar, consideremos agora o nosso terceiro e último cenário que trabalha com a variação do tamanho amostra, variação do numéro de simulações e, por fim, variação dos parâmetros. Este é o tipo de cenário é chamado de experimento Monte Carlo completo, e é fundamental para a avaliar do viés e performance de modelos probabilísticos, em geral. Assim, para melhor visualizar o funcionamento desse cenário, vamos considerar um experimento Monte Carlo com \(N = 10, 100, 1000\) simulações.
- Primeiro caso: 10 simulações:
# Simulação Monte Carlo - Distribuição Weibull
require(fitdistrplus)
# Estimador de Máxima Verossimilhança
emv.weibull <- function(x, par)
{
fit <- try(fitdist(x, 'weibull', start = list(shape = par[[1]], scale = par[[2]]))$estimate, silent = TRUE)
if(!is.numeric(fit)) fit <- NA
return(fit)
}
# Estrutura da Simulação
set.seed(1212) # Definir a semente para resultados reprodutíveis
shapes <- seq(0.5, 1.5, 0.5) # Definir os valores paramétricos para o parâmetro de forma (shape)
scales <- seq(0.5, 1.5, 0.5) # Definir os valores paramétricos para o parâmetro de escala (scale)
param <- expand.grid(shapes, scales) # Cenários
B <- 10 # Número de simulações
nmax <- 100 # Tamanho de amostra máximo
enes <- seq(10, nmax, 10) # Tamanhos de amostras
v.shape <- matrix(nrow = length(enes), ncol = nrow(param)) # Viés do estimador do parâmetro de forma (shape)
v.scale <- v.shape # Viés do estimador do parâmetro de escala (scale)
e.shape <- matrix(nrow = length(enes), ncol = nrow(param)) # Erro-quadrático-médio do estimador do parâmetro de forma (shape)
e.scale <- e.shape # Erro-quadrático-médio do estimador do parâmetro de escala (scale)
# Laço de Repetição da Simulação
for(i in 1:nrow(param))
{
k <- 1
X <- rweibull(nmax * B, shape = param[i,1], scale = param[i,2])
X <- matrix(X, ncol = B, nrow = nmax)
for(n in enes)
{
x <- data.frame(X[1:n,])
fit <- sapply(x, emv.weibull, par = param[i,])
v.shape[k,i] <- mean(fit[][1,] - param[i,1], na.rm = TRUE) # Viés: Shape
v.scale[k,i] <- mean(fit[][2,] - param[i,2], na.rm = TRUE) # Viés: Scale
e.shape[k,i] <- mean((fit[][1,] - param[i,1])^2, na.rm = TRUE) # EQM: Shape
e.scale[k,i] <- mean((fit[][2,] - param[i,2])^2, na.rm = TRUE) # EQM: Scale
k <- k + 1
cat(i, paste('Tamanho de amostra:', n), paste('Shape:', param[i,1], 'Scale:', param[i,2]), "\n") # Visualiza os Cenários
}
}## 1 Tamanho de amostra: 10 Shape: 0.5 Scale: 0.5
## 1 Tamanho de amostra: 20 Shape: 0.5 Scale: 0.5
## 1 Tamanho de amostra: 30 Shape: 0.5 Scale: 0.5
## 1 Tamanho de amostra: 40 Shape: 0.5 Scale: 0.5
## 1 Tamanho de amostra: 50 Shape: 0.5 Scale: 0.5
## 1 Tamanho de amostra: 60 Shape: 0.5 Scale: 0.5
## 1 Tamanho de amostra: 70 Shape: 0.5 Scale: 0.5
## 1 Tamanho de amostra: 80 Shape: 0.5 Scale: 0.5
## 1 Tamanho de amostra: 90 Shape: 0.5 Scale: 0.5
## 1 Tamanho de amostra: 100 Shape: 0.5 Scale: 0.5
## 2 Tamanho de amostra: 10 Shape: 1 Scale: 0.5
## 2 Tamanho de amostra: 20 Shape: 1 Scale: 0.5
## 2 Tamanho de amostra: 30 Shape: 1 Scale: 0.5
## 2 Tamanho de amostra: 40 Shape: 1 Scale: 0.5
## 2 Tamanho de amostra: 50 Shape: 1 Scale: 0.5
## 2 Tamanho de amostra: 60 Shape: 1 Scale: 0.5
## 2 Tamanho de amostra: 70 Shape: 1 Scale: 0.5
## 2 Tamanho de amostra: 80 Shape: 1 Scale: 0.5
## 2 Tamanho de amostra: 90 Shape: 1 Scale: 0.5
## 2 Tamanho de amostra: 100 Shape: 1 Scale: 0.5
## 3 Tamanho de amostra: 10 Shape: 1.5 Scale: 0.5
## 3 Tamanho de amostra: 20 Shape: 1.5 Scale: 0.5
## 3 Tamanho de amostra: 30 Shape: 1.5 Scale: 0.5
## 3 Tamanho de amostra: 40 Shape: 1.5 Scale: 0.5
## 3 Tamanho de amostra: 50 Shape: 1.5 Scale: 0.5
## 3 Tamanho de amostra: 60 Shape: 1.5 Scale: 0.5
## 3 Tamanho de amostra: 70 Shape: 1.5 Scale: 0.5
## 3 Tamanho de amostra: 80 Shape: 1.5 Scale: 0.5
## 3 Tamanho de amostra: 90 Shape: 1.5 Scale: 0.5
## 3 Tamanho de amostra: 100 Shape: 1.5 Scale: 0.5
## 4 Tamanho de amostra: 10 Shape: 0.5 Scale: 1
## 4 Tamanho de amostra: 20 Shape: 0.5 Scale: 1
## 4 Tamanho de amostra: 30 Shape: 0.5 Scale: 1
## 4 Tamanho de amostra: 40 Shape: 0.5 Scale: 1
## 4 Tamanho de amostra: 50 Shape: 0.5 Scale: 1
## 4 Tamanho de amostra: 60 Shape: 0.5 Scale: 1
## 4 Tamanho de amostra: 70 Shape: 0.5 Scale: 1
## 4 Tamanho de amostra: 80 Shape: 0.5 Scale: 1
## 4 Tamanho de amostra: 90 Shape: 0.5 Scale: 1
## 4 Tamanho de amostra: 100 Shape: 0.5 Scale: 1
## 5 Tamanho de amostra: 10 Shape: 1 Scale: 1
## 5 Tamanho de amostra: 20 Shape: 1 Scale: 1
## 5 Tamanho de amostra: 30 Shape: 1 Scale: 1
## 5 Tamanho de amostra: 40 Shape: 1 Scale: 1
## 5 Tamanho de amostra: 50 Shape: 1 Scale: 1
## 5 Tamanho de amostra: 60 Shape: 1 Scale: 1
## 5 Tamanho de amostra: 70 Shape: 1 Scale: 1
## 5 Tamanho de amostra: 80 Shape: 1 Scale: 1
## 5 Tamanho de amostra: 90 Shape: 1 Scale: 1
## 5 Tamanho de amostra: 100 Shape: 1 Scale: 1
## 6 Tamanho de amostra: 10 Shape: 1.5 Scale: 1
## 6 Tamanho de amostra: 20 Shape: 1.5 Scale: 1
## 6 Tamanho de amostra: 30 Shape: 1.5 Scale: 1
## 6 Tamanho de amostra: 40 Shape: 1.5 Scale: 1
## 6 Tamanho de amostra: 50 Shape: 1.5 Scale: 1
## 6 Tamanho de amostra: 60 Shape: 1.5 Scale: 1
## 6 Tamanho de amostra: 70 Shape: 1.5 Scale: 1
## 6 Tamanho de amostra: 80 Shape: 1.5 Scale: 1
## 6 Tamanho de amostra: 90 Shape: 1.5 Scale: 1
## 6 Tamanho de amostra: 100 Shape: 1.5 Scale: 1
## 7 Tamanho de amostra: 10 Shape: 0.5 Scale: 1.5
## 7 Tamanho de amostra: 20 Shape: 0.5 Scale: 1.5
## 7 Tamanho de amostra: 30 Shape: 0.5 Scale: 1.5
## 7 Tamanho de amostra: 40 Shape: 0.5 Scale: 1.5
## 7 Tamanho de amostra: 50 Shape: 0.5 Scale: 1.5
## 7 Tamanho de amostra: 60 Shape: 0.5 Scale: 1.5
## 7 Tamanho de amostra: 70 Shape: 0.5 Scale: 1.5
## 7 Tamanho de amostra: 80 Shape: 0.5 Scale: 1.5
## 7 Tamanho de amostra: 90 Shape: 0.5 Scale: 1.5
## 7 Tamanho de amostra: 100 Shape: 0.5 Scale: 1.5
## 8 Tamanho de amostra: 10 Shape: 1 Scale: 1.5
## 8 Tamanho de amostra: 20 Shape: 1 Scale: 1.5
## 8 Tamanho de amostra: 30 Shape: 1 Scale: 1.5
## 8 Tamanho de amostra: 40 Shape: 1 Scale: 1.5
## 8 Tamanho de amostra: 50 Shape: 1 Scale: 1.5
## 8 Tamanho de amostra: 60 Shape: 1 Scale: 1.5
## 8 Tamanho de amostra: 70 Shape: 1 Scale: 1.5
## 8 Tamanho de amostra: 80 Shape: 1 Scale: 1.5
## 8 Tamanho de amostra: 90 Shape: 1 Scale: 1.5
## 8 Tamanho de amostra: 100 Shape: 1 Scale: 1.5
## 9 Tamanho de amostra: 10 Shape: 1.5 Scale: 1.5
## 9 Tamanho de amostra: 20 Shape: 1.5 Scale: 1.5
## 9 Tamanho de amostra: 30 Shape: 1.5 Scale: 1.5
## 9 Tamanho de amostra: 40 Shape: 1.5 Scale: 1.5
## 9 Tamanho de amostra: 50 Shape: 1.5 Scale: 1.5
## 9 Tamanho de amostra: 60 Shape: 1.5 Scale: 1.5
## 9 Tamanho de amostra: 70 Shape: 1.5 Scale: 1.5
## 9 Tamanho de amostra: 80 Shape: 1.5 Scale: 1.5
## 9 Tamanho de amostra: 90 Shape: 1.5 Scale: 1.5
## 9 Tamanho de amostra: 100 Shape: 1.5 Scale: 1.5
# Gráficos
par(mfrow = c(1,2))
matplot(v.shape, type = 'b', ylab = 'Vies: shape', xlab = 'Tamanho de amostra')
matplot(v.scale, type = 'b', ylab = 'Vies: scale', xlab = 'Tamanho de amostra')par(mfrow = c(1,2))
matplot(e.shape, type = 'b', ylab = 'EQM: shape', xlab = 'Tamanho de amostra')
matplot(e.scale, type = 'b', ylab = 'EQM: scale', xlab = 'Tamanho de amostra')- Segundo caso: 100 simulações:
# Simulação Monte Carlo - Distribuição Weibull
require(fitdistrplus)
# Estimador de Máxima Verossimilhança
emv.weibull <- function(x, par)
{
fit <- try(fitdist(x, 'weibull', start = list(shape = par[[1]], scale = par[[2]]))$estimate, silent = TRUE)
if(!is.numeric(fit)) fit <- NA
return(fit)
}
# Estrutura da Simulação
set.seed(1212) # Definir a semente para resultados reprodutíveis
shapes <- seq(0.5, 1.5, 0.5) # Definir os valores paramétricos para o parâmetro de forma (shape)
scales <- seq(0.5, 1.5, 0.5) # Definir os valores paramétricos para o parâmetro de escala (scale)
param <- expand.grid(shapes, scales) # Cenários
B <- 100 # Número de simulações
nmax <- 100 # Tamanho de amostra máximo
enes <- seq(10, nmax, 10) # Tamanhos de amostras
v.shape <- matrix(nrow = length(enes), ncol = nrow(param)) # Viés do estimador do parâmetro de forma (shape)
v.scale <- v.shape # Viés do estimador do parâmetro de escala (scale)
e.shape <- matrix(nrow = length(enes), ncol = nrow(param)) # Erro-quadrático-médio do estimador do parâmetro de forma (shape)
e.scale <- e.shape # Erro-quadrático-médio do estimador do parâmetro de escala (scale)
# Laço de Repetição da Simulação
for(i in 1:nrow(param))
{
k <- 1
X <- rweibull(nmax * B, shape = param[i,1], scale = param[i,2])
X <- matrix(X, ncol = B, nrow = nmax)
for(n in enes)
{
x <- data.frame(X[1:n,])
fit <- sapply(x, emv.weibull, par = param[i,])
v.shape[k,i] <- mean(fit[][1,] - param[i,1], na.rm = TRUE) # Viés: Shape
v.scale[k,i] <- mean(fit[][2,] - param[i,2], na.rm = TRUE) # Viés: Scale
e.shape[k,i] <- mean((fit[][1,] - param[i,1])^2, na.rm = TRUE) # EQM: Shape
e.scale[k,i] <- mean((fit[][2,] - param[i,2])^2, na.rm = TRUE) # EQM: Scale
k <- k + 1
cat(i, paste('Tamanho de amostra:', n), paste('Shape:', param[i,1], 'Scale:', param[i,2]), "\n") # Visualiza os Cenários
}
}## 1 Tamanho de amostra: 10 Shape: 0.5 Scale: 0.5
## 1 Tamanho de amostra: 20 Shape: 0.5 Scale: 0.5
## 1 Tamanho de amostra: 30 Shape: 0.5 Scale: 0.5
## 1 Tamanho de amostra: 40 Shape: 0.5 Scale: 0.5
## 1 Tamanho de amostra: 50 Shape: 0.5 Scale: 0.5
## 1 Tamanho de amostra: 60 Shape: 0.5 Scale: 0.5
## 1 Tamanho de amostra: 70 Shape: 0.5 Scale: 0.5
## 1 Tamanho de amostra: 80 Shape: 0.5 Scale: 0.5
## 1 Tamanho de amostra: 90 Shape: 0.5 Scale: 0.5
## 1 Tamanho de amostra: 100 Shape: 0.5 Scale: 0.5
## 2 Tamanho de amostra: 10 Shape: 1 Scale: 0.5
## 2 Tamanho de amostra: 20 Shape: 1 Scale: 0.5
## 2 Tamanho de amostra: 30 Shape: 1 Scale: 0.5
## 2 Tamanho de amostra: 40 Shape: 1 Scale: 0.5
## 2 Tamanho de amostra: 50 Shape: 1 Scale: 0.5
## 2 Tamanho de amostra: 60 Shape: 1 Scale: 0.5
## 2 Tamanho de amostra: 70 Shape: 1 Scale: 0.5
## 2 Tamanho de amostra: 80 Shape: 1 Scale: 0.5
## 2 Tamanho de amostra: 90 Shape: 1 Scale: 0.5
## 2 Tamanho de amostra: 100 Shape: 1 Scale: 0.5
## 3 Tamanho de amostra: 10 Shape: 1.5 Scale: 0.5
## 3 Tamanho de amostra: 20 Shape: 1.5 Scale: 0.5
## 3 Tamanho de amostra: 30 Shape: 1.5 Scale: 0.5
## 3 Tamanho de amostra: 40 Shape: 1.5 Scale: 0.5
## 3 Tamanho de amostra: 50 Shape: 1.5 Scale: 0.5
## 3 Tamanho de amostra: 60 Shape: 1.5 Scale: 0.5
## 3 Tamanho de amostra: 70 Shape: 1.5 Scale: 0.5
## 3 Tamanho de amostra: 80 Shape: 1.5 Scale: 0.5
## 3 Tamanho de amostra: 90 Shape: 1.5 Scale: 0.5
## 3 Tamanho de amostra: 100 Shape: 1.5 Scale: 0.5
## 4 Tamanho de amostra: 10 Shape: 0.5 Scale: 1
## 4 Tamanho de amostra: 20 Shape: 0.5 Scale: 1
## 4 Tamanho de amostra: 30 Shape: 0.5 Scale: 1
## 4 Tamanho de amostra: 40 Shape: 0.5 Scale: 1
## 4 Tamanho de amostra: 50 Shape: 0.5 Scale: 1
## 4 Tamanho de amostra: 60 Shape: 0.5 Scale: 1
## 4 Tamanho de amostra: 70 Shape: 0.5 Scale: 1
## 4 Tamanho de amostra: 80 Shape: 0.5 Scale: 1
## 4 Tamanho de amostra: 90 Shape: 0.5 Scale: 1
## 4 Tamanho de amostra: 100 Shape: 0.5 Scale: 1
## 5 Tamanho de amostra: 10 Shape: 1 Scale: 1
## 5 Tamanho de amostra: 20 Shape: 1 Scale: 1
## 5 Tamanho de amostra: 30 Shape: 1 Scale: 1
## 5 Tamanho de amostra: 40 Shape: 1 Scale: 1
## 5 Tamanho de amostra: 50 Shape: 1 Scale: 1
## 5 Tamanho de amostra: 60 Shape: 1 Scale: 1
## 5 Tamanho de amostra: 70 Shape: 1 Scale: 1
## 5 Tamanho de amostra: 80 Shape: 1 Scale: 1
## 5 Tamanho de amostra: 90 Shape: 1 Scale: 1
## 5 Tamanho de amostra: 100 Shape: 1 Scale: 1
## 6 Tamanho de amostra: 10 Shape: 1.5 Scale: 1
## 6 Tamanho de amostra: 20 Shape: 1.5 Scale: 1
## 6 Tamanho de amostra: 30 Shape: 1.5 Scale: 1
## 6 Tamanho de amostra: 40 Shape: 1.5 Scale: 1
## 6 Tamanho de amostra: 50 Shape: 1.5 Scale: 1
## 6 Tamanho de amostra: 60 Shape: 1.5 Scale: 1
## 6 Tamanho de amostra: 70 Shape: 1.5 Scale: 1
## 6 Tamanho de amostra: 80 Shape: 1.5 Scale: 1
## 6 Tamanho de amostra: 90 Shape: 1.5 Scale: 1
## 6 Tamanho de amostra: 100 Shape: 1.5 Scale: 1
## 7 Tamanho de amostra: 10 Shape: 0.5 Scale: 1.5
## 7 Tamanho de amostra: 20 Shape: 0.5 Scale: 1.5
## 7 Tamanho de amostra: 30 Shape: 0.5 Scale: 1.5
## 7 Tamanho de amostra: 40 Shape: 0.5 Scale: 1.5
## 7 Tamanho de amostra: 50 Shape: 0.5 Scale: 1.5
## 7 Tamanho de amostra: 60 Shape: 0.5 Scale: 1.5
## 7 Tamanho de amostra: 70 Shape: 0.5 Scale: 1.5
## 7 Tamanho de amostra: 80 Shape: 0.5 Scale: 1.5
## 7 Tamanho de amostra: 90 Shape: 0.5 Scale: 1.5
## 7 Tamanho de amostra: 100 Shape: 0.5 Scale: 1.5
## 8 Tamanho de amostra: 10 Shape: 1 Scale: 1.5
## 8 Tamanho de amostra: 20 Shape: 1 Scale: 1.5
## 8 Tamanho de amostra: 30 Shape: 1 Scale: 1.5
## 8 Tamanho de amostra: 40 Shape: 1 Scale: 1.5
## 8 Tamanho de amostra: 50 Shape: 1 Scale: 1.5
## 8 Tamanho de amostra: 60 Shape: 1 Scale: 1.5
## 8 Tamanho de amostra: 70 Shape: 1 Scale: 1.5
## 8 Tamanho de amostra: 80 Shape: 1 Scale: 1.5
## 8 Tamanho de amostra: 90 Shape: 1 Scale: 1.5
## 8 Tamanho de amostra: 100 Shape: 1 Scale: 1.5
## 9 Tamanho de amostra: 10 Shape: 1.5 Scale: 1.5
## 9 Tamanho de amostra: 20 Shape: 1.5 Scale: 1.5
## 9 Tamanho de amostra: 30 Shape: 1.5 Scale: 1.5
## 9 Tamanho de amostra: 40 Shape: 1.5 Scale: 1.5
## 9 Tamanho de amostra: 50 Shape: 1.5 Scale: 1.5
## 9 Tamanho de amostra: 60 Shape: 1.5 Scale: 1.5
## 9 Tamanho de amostra: 70 Shape: 1.5 Scale: 1.5
## 9 Tamanho de amostra: 80 Shape: 1.5 Scale: 1.5
## 9 Tamanho de amostra: 90 Shape: 1.5 Scale: 1.5
## 9 Tamanho de amostra: 100 Shape: 1.5 Scale: 1.5
# Gráficos
par(mfrow = c(1,2))
matplot(v.shape, type = 'b', ylab = 'Vies: shape', xlab = 'Tamanho de amostra')
matplot(v.scale, type = 'b', ylab = 'Vies: scale', xlab = 'Tamanho de amostra')par(mfrow = c(1,2))
matplot(e.shape, type = 'b', ylab = 'EQM: shape', xlab = 'Tamanho de amostra')
matplot(e.scale, type = 'b', ylab = 'EQM: scale', xlab = 'Tamanho de amostra')- Terceiro caso: 1000 simulações:
# Simulação Monte Carlo - Distribuição Weibull
require(fitdistrplus)
# Estimador de Máxima Verossimilhança
emv.weibull <- function(x, par)
{
fit <- try(fitdist(x, 'weibull', start = list(shape = par[[1]], scale = par[[2]]))$estimate, silent = TRUE)
if(!is.numeric(fit)) fit <- NA
return(fit)
}
# Estrutura da Simulação
set.seed(1212) # Definir a semente para resultados reprodutíveis
shapes <- seq(0.5, 1.5, 0.5) # Definir os valores paramétricos para o parâmetro de forma (shape)
scales <- seq(0.5, 1.5, 0.5) # Definir os valores paramétricos para o parâmetro de escala (scale)
param <- expand.grid(shapes, scales) # Cenários
B <- 1000 # Número de simulações
nmax <- 100 # Tamanho de amostra máximo
enes <- seq(10, nmax, 10) # Tamanhos de amostras
v.shape <- matrix(nrow = length(enes), ncol = nrow(param)) # Viés do estimador do parâmetro de forma (shape)
v.scale <- v.shape # Viés do estimador do parâmetro de escala (scale)
e.shape <- matrix(nrow = length(enes), ncol = nrow(param)) # Erro-quadrático-médio do estimador do parâmetro de forma (shape)
e.scale <- e.shape # Erro-quadrático-médio do estimador do parâmetro de escala (scale)
# Laço de Repetição da Simulação
for(i in 1:nrow(param))
{
k <- 1
X <- rweibull(nmax * B, shape = param[i,1], scale = param[i,2])
X <- matrix(X, ncol = B, nrow = nmax)
for(n in enes)
{
x <- data.frame(X[1:n,])
fit <- sapply(x, emv.weibull, par = param[i,])
v.shape[k,i] <- mean(fit[][1,] - param[i,1], na.rm = TRUE) # Viés: Shape
v.scale[k,i] <- mean(fit[][2,] - param[i,2], na.rm = TRUE) # Viés: Scale
e.shape[k,i] <- mean((fit[][1,] - param[i,1])^2, na.rm = TRUE) # EQM: Shape
e.scale[k,i] <- mean((fit[][2,] - param[i,2])^2, na.rm = TRUE) # EQM: Scale
k <- k + 1
cat(i, paste('Tamanho de amostra:', n), paste('Shape:', param[i,1], 'Scale:', param[i,2]), "\n") # Visualiza os Cenários
}
}## 1 Tamanho de amostra: 10 Shape: 0.5 Scale: 0.5
## 1 Tamanho de amostra: 20 Shape: 0.5 Scale: 0.5
## 1 Tamanho de amostra: 30 Shape: 0.5 Scale: 0.5
## 1 Tamanho de amostra: 40 Shape: 0.5 Scale: 0.5
## 1 Tamanho de amostra: 50 Shape: 0.5 Scale: 0.5
## 1 Tamanho de amostra: 60 Shape: 0.5 Scale: 0.5
## 1 Tamanho de amostra: 70 Shape: 0.5 Scale: 0.5
## 1 Tamanho de amostra: 80 Shape: 0.5 Scale: 0.5
## 1 Tamanho de amostra: 90 Shape: 0.5 Scale: 0.5
## 1 Tamanho de amostra: 100 Shape: 0.5 Scale: 0.5
## 2 Tamanho de amostra: 10 Shape: 1 Scale: 0.5
## 2 Tamanho de amostra: 20 Shape: 1 Scale: 0.5
## 2 Tamanho de amostra: 30 Shape: 1 Scale: 0.5
## 2 Tamanho de amostra: 40 Shape: 1 Scale: 0.5
## 2 Tamanho de amostra: 50 Shape: 1 Scale: 0.5
## 2 Tamanho de amostra: 60 Shape: 1 Scale: 0.5
## 2 Tamanho de amostra: 70 Shape: 1 Scale: 0.5
## 2 Tamanho de amostra: 80 Shape: 1 Scale: 0.5
## 2 Tamanho de amostra: 90 Shape: 1 Scale: 0.5
## 2 Tamanho de amostra: 100 Shape: 1 Scale: 0.5
## 3 Tamanho de amostra: 10 Shape: 1.5 Scale: 0.5
## 3 Tamanho de amostra: 20 Shape: 1.5 Scale: 0.5
## 3 Tamanho de amostra: 30 Shape: 1.5 Scale: 0.5
## 3 Tamanho de amostra: 40 Shape: 1.5 Scale: 0.5
## 3 Tamanho de amostra: 50 Shape: 1.5 Scale: 0.5
## 3 Tamanho de amostra: 60 Shape: 1.5 Scale: 0.5
## 3 Tamanho de amostra: 70 Shape: 1.5 Scale: 0.5
## 3 Tamanho de amostra: 80 Shape: 1.5 Scale: 0.5
## 3 Tamanho de amostra: 90 Shape: 1.5 Scale: 0.5
## 3 Tamanho de amostra: 100 Shape: 1.5 Scale: 0.5
## 4 Tamanho de amostra: 10 Shape: 0.5 Scale: 1
## 4 Tamanho de amostra: 20 Shape: 0.5 Scale: 1
## 4 Tamanho de amostra: 30 Shape: 0.5 Scale: 1
## 4 Tamanho de amostra: 40 Shape: 0.5 Scale: 1
## 4 Tamanho de amostra: 50 Shape: 0.5 Scale: 1
## 4 Tamanho de amostra: 60 Shape: 0.5 Scale: 1
## 4 Tamanho de amostra: 70 Shape: 0.5 Scale: 1
## 4 Tamanho de amostra: 80 Shape: 0.5 Scale: 1
## 4 Tamanho de amostra: 90 Shape: 0.5 Scale: 1
## 4 Tamanho de amostra: 100 Shape: 0.5 Scale: 1
## 5 Tamanho de amostra: 10 Shape: 1 Scale: 1
## 5 Tamanho de amostra: 20 Shape: 1 Scale: 1
## 5 Tamanho de amostra: 30 Shape: 1 Scale: 1
## 5 Tamanho de amostra: 40 Shape: 1 Scale: 1
## 5 Tamanho de amostra: 50 Shape: 1 Scale: 1
## 5 Tamanho de amostra: 60 Shape: 1 Scale: 1
## 5 Tamanho de amostra: 70 Shape: 1 Scale: 1
## 5 Tamanho de amostra: 80 Shape: 1 Scale: 1
## 5 Tamanho de amostra: 90 Shape: 1 Scale: 1
## 5 Tamanho de amostra: 100 Shape: 1 Scale: 1
## 6 Tamanho de amostra: 10 Shape: 1.5 Scale: 1
## 6 Tamanho de amostra: 20 Shape: 1.5 Scale: 1
## 6 Tamanho de amostra: 30 Shape: 1.5 Scale: 1
## 6 Tamanho de amostra: 40 Shape: 1.5 Scale: 1
## 6 Tamanho de amostra: 50 Shape: 1.5 Scale: 1
## 6 Tamanho de amostra: 60 Shape: 1.5 Scale: 1
## 6 Tamanho de amostra: 70 Shape: 1.5 Scale: 1
## 6 Tamanho de amostra: 80 Shape: 1.5 Scale: 1
## 6 Tamanho de amostra: 90 Shape: 1.5 Scale: 1
## 6 Tamanho de amostra: 100 Shape: 1.5 Scale: 1
## 7 Tamanho de amostra: 10 Shape: 0.5 Scale: 1.5
## 7 Tamanho de amostra: 20 Shape: 0.5 Scale: 1.5
## 7 Tamanho de amostra: 30 Shape: 0.5 Scale: 1.5
## 7 Tamanho de amostra: 40 Shape: 0.5 Scale: 1.5
## 7 Tamanho de amostra: 50 Shape: 0.5 Scale: 1.5
## 7 Tamanho de amostra: 60 Shape: 0.5 Scale: 1.5
## 7 Tamanho de amostra: 70 Shape: 0.5 Scale: 1.5
## 7 Tamanho de amostra: 80 Shape: 0.5 Scale: 1.5
## 7 Tamanho de amostra: 90 Shape: 0.5 Scale: 1.5
## 7 Tamanho de amostra: 100 Shape: 0.5 Scale: 1.5
## 8 Tamanho de amostra: 10 Shape: 1 Scale: 1.5
## 8 Tamanho de amostra: 20 Shape: 1 Scale: 1.5
## 8 Tamanho de amostra: 30 Shape: 1 Scale: 1.5
## 8 Tamanho de amostra: 40 Shape: 1 Scale: 1.5
## 8 Tamanho de amostra: 50 Shape: 1 Scale: 1.5
## 8 Tamanho de amostra: 60 Shape: 1 Scale: 1.5
## 8 Tamanho de amostra: 70 Shape: 1 Scale: 1.5
## 8 Tamanho de amostra: 80 Shape: 1 Scale: 1.5
## 8 Tamanho de amostra: 90 Shape: 1 Scale: 1.5
## 8 Tamanho de amostra: 100 Shape: 1 Scale: 1.5
## 9 Tamanho de amostra: 10 Shape: 1.5 Scale: 1.5
## 9 Tamanho de amostra: 20 Shape: 1.5 Scale: 1.5
## 9 Tamanho de amostra: 30 Shape: 1.5 Scale: 1.5
## 9 Tamanho de amostra: 40 Shape: 1.5 Scale: 1.5
## 9 Tamanho de amostra: 50 Shape: 1.5 Scale: 1.5
## 9 Tamanho de amostra: 60 Shape: 1.5 Scale: 1.5
## 9 Tamanho de amostra: 70 Shape: 1.5 Scale: 1.5
## 9 Tamanho de amostra: 80 Shape: 1.5 Scale: 1.5
## 9 Tamanho de amostra: 90 Shape: 1.5 Scale: 1.5
## 9 Tamanho de amostra: 100 Shape: 1.5 Scale: 1.5
# Gráficos
par(mfrow = c(1,2))
matplot(v.shape, type = 'b', ylab = 'Vies: shape', xlab = 'Tamanho de amostra')
matplot(v.scale, type = 'b', ylab = 'Vies: scale', xlab = 'Tamanho de amostra')par(mfrow = c(1,2))
matplot(e.shape, type = 'b', ylab = 'EQM: shape', xlab = 'Tamanho de amostra')
matplot(e.scale, type = 'b', ylab = 'EQM: scale', xlab = 'Tamanho de amostra')Referências Bibliográficas
- MARTINEZ, E. Z. Bioestatística Para os Cursos de Graduação da Área da Saúde. Editora Blucher, 2015.
- PAGANO, M., GAUVREAU, K. Princípios de Bioestatística. 2ª Edição. São Paulo: Cengage Learning, 2011.
- DANIEL, W. W., CROSS, C. L. Biostatistics: A Foundation for Analysis in the Health Sciences. Wiley, 2018.
- ROSNER, B. Fundamentos de Bioestatística. Cengage Learning Brasil, 2018.
- CALLEGARI-JACQUES, S. M. Bioestatística: Princípios e Aplicações. Grupo A, 2003.
- MAGALHÃES, M. N., DE LIMA, A. C. P. Noções de Probabilidade e Estatística. Editora da Universidade de São Paulo, 2002.
- GIVENS, G. H.; HOETING, J. A. Computational statistics. John Wiley & Sons, 2012.
- FREUND, J. E. Estatística Aplicada: Economia, Administração e Contabilidade. Bookman Editora, 2009.
- DOANE, D. P.; SEWARD, L. E. Estatística Aplicada à Administração e Economia. Grupo A, 2014.
- SEWARD, L. E., DOANE, D. P. Estatística Aplicada à Administração e Economia. AMGH editora, 2014.
- SWEENEY, D J., WILLIAMS, T. A., ANDERSON, D. R. Estatística Aplicada à Administração e Economia. 5ª Edição. São Paulo: Cengage Learning, 2020.
- LEVIN, J., FOX, J. A., FORDE, D. R. Estatística Para Ciências Humanas. São Paulo: Pearson, 2012.
- VARIAN, H. R. Big Data: New Tricks for Econometrics. The Journal of Economic Perspectives 28 (2). American Economic Association: 3–27. 2014.
- PEREIRA, T. N. Cointegração: Uma relação de equilíbrio de longo prazo. 2013.
- BOWLES, M. Ensemble Packages in R. See: http://blog.revolutionanalytics.com/2014/04/Ensemble-Packages-in-R.html. 2015.